Java集合 -- ArrayList源码阅读

定义

一种可以动态增长和缩减的索引序列

ArrayList 数据结构

    private static final long serialVersionUID = 8683452581122892189L;
    //默认除湿容量
    private static final int DEFAULT_CAPACITY = 10;
	//空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
	//空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
	//存储数据的数组
    transient Object[] elementData; // non-private to simplify nested class access
	//数组大小
    private int size;

ArrayList 中初始化了两个空数组和一个存储数据的数组 elementData以及定义了大小size。

构造方法

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

三个构造方法,空参,知道数组大小和初始化时传递数组。

下面从ArrayList增删改查操作查看ArrayList具体实现

        ArrayList arrayList = new ArrayList();
        arrayList.add(1);

1、add(E e)方法源码如下:

    public boolean add(E e) {
    	//初始化elementData 内存
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //添加到数组中,size加1
        elementData[size++] = e;
        return true;
    }

可以看到add()方法把添加的对象放到elementData数组中。

那么ensureCapacityInternal()方法又是干嘛的?

	//计算数组内部容量,当超过数组允许的容量时,新建一个数组接收
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        //当最小容量大于数组的长度时,扩充数组的长度
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
	//计算数组真实(最小)容量
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        	//如果实际size小于默认容量10,则返回默认大小10
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
	//扩充数组的容量
    private void grow(int minCapacity) {
    	//老的容量
        int oldCapacity = elementData.length;
        //新的容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //拷贝数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
   private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

因为ArrayList中实际存储数据的数组默认大小是10,所以当我们存储的数据大于10的时候,就要给数组扩充容量,其实就是把旧的数组复制到了一个容量更大的新数组中。

arrayList.add(1,1);

2、 add(int index, E element)

    public void add(int index, E element) {
    	//校验是否IndexOutOfBoundsException
        rangeCheckForAdd(index);
		//和上面一样,校验数组容量是否足够,否则扩充容量
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //数组拷贝
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        //赋值
        elementData[index] = element;
        //大小增加
        size++;
    }

和上面相比,这里面多了一个数组拷贝 System.arraycopy():

    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

各参数含义:

  • src:原数组
  • srcPos:从原数组起始位置开始拷贝
  • dest: 目标数组
  • destPos:拷贝到目标数组的位置
  • length:copy数组的长度

下面addAll方法更好理解arraycopy

3、addAll()

    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        //这里更好理解arraycopy
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

删除

1、remove(Object o)

    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

可以看到上面是一些逻辑处理,真正删除的地方应该在 fastRemove()方法中

    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
        	//拷贝index+1后面的数据,放到index位置
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //将最后一项置空
        elementData[--size] = null; // clear to let GC do its work
    }

2、remove(int index)

    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;
    }

这里面其实还是数组的拷贝。

3、clear

    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;
            
        size = 0;
    }

clear中for循环把每个元素都置空并且把size赋值0

ArrayList可以通过set(int index, E element)方法来设置和修改数组

    public E set(int index, E element) {
    	//IndexOutOfBoundsException校验
        rangeCheck(index);

        E oldValue = elementData(index);
        //赋值
        elementData[index] = element;
        return oldValue;
    }

1、通过indexOf(Object o)来查询对象的位置。

    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

如果存在的话就返回位置,不存在则返回-1。

2、contains(Object o)

   public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

可以看到contains其实也是调用indexOf方法

get()

    public E get(int index) {
    	//校验下标是否越界
        rangeCheck(index);

        return elementData(index);
    }

可以看到ArrayList的get方法十分简单高效,直接就能在数组中获取。而不需要像LinkedList 通过循环获取。这也是ArrayList的一个性能优势。

总结

ArrayList 内存实现是一个数组,因此数据获取和设置的效率比较高,因为有数组的拷贝与扩容,所以数据的删除与添加效率会相对低下。

发布了82 篇原创文章 · 获赞 16 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/sjdjdjdjahd/article/details/100765544