Java学习——ArrayList的源码解析

ArrayList是JAVA的一种泛型容器,它实际上是一个用数组实现的链表,今天解析一下ArrayList的源码,并写个简单的实现。

ArrayList的依赖关系

首先我们来看ArrayList的依赖继承关系:
在这里插入图片描述
通过ArrayList继承实现的接口说明了该类具有以下特性:

  • ArrayList具有链表的特性
  • Collection接口表示ArrayList是一个数据集合,但是这个数据集合没有位置和顺序关系,因此ArrayList实现了类似isEmpty,contains的方法
  • RandomAccess接口说明了ArrayList支持随机访问,这是因为底层是用数组实现,天然支持随机访问
  • Iterable接口表示ArrayList支持迭代访问,可以使用foreach语法进行数据的遍历
  • Serializable接口表示ArrayList的数据支持序列化
  • Cloneable接口表示ArrayList支持对象的克隆

ArrayList原理解析

ArrayList是通过数组实现,该类内部定义了一个Object数组用于存放数据,由于存放的数据类型是一个泛型,因此只能将数组定义为Object[]类型,这是因为Object类是所有类的父类,因此不管什么类型的对象都能赋值给Object的变量。

 transient Object[] elementData;
 
 public ArrayList(int initialCapacity) 
 public ArrayList() 
 public ArrayList(Collection<? extends E> c)

ArrayList的构造函数有三种,如果没有给定初始的数组大小,那么将创建一个空的数组;如果给定数组初始大小,那么将创建对应大小的数组;也支持给定一个Collection对象,那么该对象内的数据将赋值到elementData数组内。

ArrayList数据的添加

ArrayList调用add方法添加数据时,会先检查一下当前数组大小是否具有足够的空间。

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

当数组空间不足时,最终会跳转到grow方法进行数组扩容,该方法是计算原空间大小*1.5与minCapacity之间的最大值作为新的数组的大小,然后将原数组的数据都拷贝至新的数组。

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
ArrayList数据的删除

ArrayList的底层实现是数组,数据连续排放,所以在删除数据时,效率较低,需要进行数据的挪动。

public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
    

假设数组大小是size,要删除的数据下标是index,那么下标大于index的数据都要向左挪动一位,并将原来的最后一位置为空。System.arraycopy执行的功能实际上就是进行数据的平移。

ArrayList转数组

由于Array的底层实现是一个数组,因此,ArrayList可以将维护的数组返回,但是由于这是一个泛型容器,如果直接将底层的Object[]数组强制转化为T[]返回,编译器会报错,java不允许父类对象强转为子类变量。

 public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

toArray方法实现对具体类型数组的转化,该方法有一个T[] a的参数,如果a的大小足够存放所有数据,那么直接进行数据的复制并返回结果。

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

如果a数组的大小不够大,那么需要创建一个新的T[]数组进行数据的复制。Array.newInstance(newType.getComponentType(), newLength)是利用了反射,通过给定的对象类型,创建一个指定大小,指定对象类型的数组,因为创建的数组本身就是T[]类型的,因此这里的强转是没有问题的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值