JDK源码解析--ArrayList

一、ArrayList简介

  1. ArrayList是用一个数组实现的集合,支持随机访问,元素有序且可以重复。
  2. ArrayList是一种变长的集合类,基于定长数组实现。
  3. ArrayList允许空值和重复元素,当在其添加的元素数量大于其底层数组容量是,其会通过扩容机制重新生成一个更大的数据。
  4. ArrayList底层基于数组实现,所以可以保证在0(1)复杂度下完成随机查找操作。
  5. ArrayList并非线程安全类,在并发环境下,多个线程同时操作ArrayList,会引发不可预知的异常或错误。

RandomAccess接口:标记接口,一边用于list的实现,表示支持快速随机访问

Cloneable:同标记接口,表示可以被克隆

Serializable:标记接口,表示可以被序列化

List:List类集合的上层接口,定义了实现该接口的类都必须要实现的一组方法

二、方法解析

1.无参构造

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

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

这里其实只是相当于创建了一个空数组,并不是认为的10。

2.有参构造

 private static final Object[] EMPTY_ELEMENTDATA = {};


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

这时候是根据传进来的数组长度进行创建,如果为0的话,则会是一个空数据,小于0则抛异常

3.重载:ArrayList(Collection<?extends E> c)

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

将已有的集合赋值到ArraryList集合中

结合上面几点产生疑惑,无参构造和0长度构造的区别?

结论:

当一个无参构造的ArrayList添加第一个元素时,会给数组扩容到10并在剩余空间中都存放null

0长度构造的ArrayList则是添加该元素就结束了

 原理:

//calculateCapacity
//每次元素变动,比如add,会调用该函数判断容量情况
private static int calculateCapacity(Object[] elementData, int minCapacity)
{
//定义default empty数组的意义就在这里!用于扩容时判断当初采用的是哪种构造函数
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//如果是无参的构造函数,用的就是该default empty
//那么第一次add时候,容量取default和min中较大者
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
//如果是另外两个构造函数,比如指定容量为5,或者初始参数collection为5
//那就直接返回5,一定程度上,节约了内存空间
return minCapacity;
}

4.添加元素

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


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

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

在添加元素之前会计算是否需要扩容,根据当前所需要的最小容量来进行计算,如果需要的最小容量减去当前数组长度大于0,也就是不能满足的时候,就会进行扩容。

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

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

扩容的时候会将之前的容量转化成2进制右移一位,就像对于除以2,然后再加上之前的旧容量,也就相当于扩大了1.5倍。之后再判断是否满足容量以及是否超过最大长度,也就是int的最大值-8,之后就是创建出这个数据并将原来的值拷贝进其中并返回即可。

5,删除元素

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

首先先是检查是否有索引越界,然后拿到需要删除的元素,接着去判断是否为最后一个元素,因为如果是最后一个元素的话,直接将其置为null即可,不需要将index+1的元素移到index的位置上;如果不是最后一个元素的话,会通过arraycopy对数组进行自身的拷贝

6.修改元素 

public E set(int index, E element) {
        rangeCheck(index);

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

简单的获取并覆盖而已,如果index是负数则会抛出异常

7.查找元素

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

        return elementData(index);
    }

8.迭代器遍历

    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

前面在新增元素add() 和 删除元素 remove() 时,我们可以看到 modCount++,修改的set是没有的

也就是说不能再迭代器进行元素迭代时进行增加和删除操作,否则会抛出异常。

另外再进行next方法调用时,因为会进行checkForComodification调用,如果同时进行增加和删除则会抛异常,解决方法是不调用 ArrayList.remove() 方法,而是调用迭代器的remove方法

最后一点,我们所熟知的增强for其实就是迭代器便利的,这点我们可以通过反编译看到效果。

ArrayList list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
String str;
for (Iterator iterator1 = list.iterator(); iterator1.hasNext();
System.out.print((new StringBuilder(String.valueOf(str))).append("
").toString()))
str = (String)iterator1.next();

以上就是ArrayList类中重要的方法已经原理


版权声明:本文为m0_51464746原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。