ArrayList & LinkedList源码讲解

188 阅读5分钟

failFast机制:这个机制主要用于迭代器、加强for循环等相关功能,也就是一个线程在迭代一个容器 时,如果其他线程改变了容器内的元素,迭代的这个线程会抛 出“ConcurrentModificationException”异常

ArrayList LinkedList 均适用了此机制

ArrayList

ArrayList具有数组的特点:

  • 可快速查询, 可通过下标进行查询
  • 插入慢

因为ArrayList需要随时进行Object数组容量大小检查,如果数组容量不能满足新插入元素所需要的大小需求,那么就会触发自动扩容,将大小扩展为原来的1.5倍,在将原来的数组拷贝到新的数组中,然后在添加元素,删除元素时也可能会涉及到数组拷贝

不是线程安全的, 因为内部维护了modCount变量, 此变量并没有使用volatile进行修饰, 因此当多个线程进行操作arrayList时, modCount的值将无法保证一致, 因此抛出ConcurrentModificationException异常(因为在进行读写之前均会对modCount进行校验)

与之对应的Vector 是线程安全的,因为在很多方法上都使用了synchronized关键字, 因此性能较ArrayList较低; 底层也是使用的数组方式.(很少使用)

方法定义

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

重要的成员变量

private static final int DEFAULT_CAPACITY = 10; // 默认初始化大小
protected transient int modCount = 0; // 记录对象被修改的次数
transient Object[] elementData; // 使用数据结构

元素最大值

/**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;


扩容

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);// oldCapacity >> 1 原来数组的一半, 也就是扩容为原来的1.5倍 使用位运算效率更高
        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);
    }

浅拷贝的Clone

此方法并不是对应List接口中定义的方法;

因为是浅拷贝, 因此新的对象与老对象同时指向同一快内存, 无论修改那个对应的另一个的内容也会修改

/**
     * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The
     * elements themselves are not copied.)
     *
     * @return a clone of this <tt>ArrayList</tt> instance
     */
    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

迭代器

如果一个ArrayList对象生成对应的迭代器之后, 并不能在使用add()方法, 如果在使用, 则在使用迭代器进行遍历的时候将会在第2处出现错误;

原因: 因为一个对象当调用Iterator iterator = al.iterator();当时候会对Itr类进行初始化, 同时会为新的Itr对象中的成员变量进行初始化 也就是

  • cursor为int默认值0
  • lastRet 初始化值为-1
  • 最重要的是 expectedModCount 初始化的值会获取对应的ArrayList (al)对象的modCount的值

但是当调用al.iterator()此方法后再次调用al.add()方法 al中的modCount值会自增, 但是已生成的的迭代器中的expectedModCount的值没有进行更新, 因此在调用next()时会调用checkForComodification()进行校验expectedModCount 与 modCount的值是否相等, 如果不相等则直接抛出异常

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
  ...
   public Iterator<E> iterator() {
        return new Itr();
    }
	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; // -------第1处

        Itr() {}

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

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();// -------- 第2处
            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; // 使用迭代器删除ArrayList中的元素时需要修改迭代器中expextedModCount的值, 否则删除后在使用next()方法会出错
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        ...
        final void checkForComodification() {
            if (modCount != expectedModCount)// modCount 是ArrayList的成员变量, 用于记录此对象修改的次数
                throw new ConcurrentModificationException();
        }
    }
}

subList

经常问的问题: 为什么subList对象同时也会修改原是的ArrayList对象?

主要的原因:SubList是ArrayList的一个内部类

从代码上看: 在1⃣️处我们可以看到最终调用了parent.add(), 通过2⃣️ 3⃣️ 可以看的出来parent就是对应的Ar rayList对象, 因此subList进行增加元素 同时原始arrayList也会变化;

从SubList类的定义中可以看出是AbstractList的子类, 但是SubList仅仅只重写了public void add(int index, E element) , 而第一个参数index 是SubList对象中的size大小

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
  ...
    public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);// ------- 2⃣️
    }
   private class SubList extends AbstractList<E> implements RandomAccess {
        private final AbstractList<E> parent;
        private final int parentOffset;
        private final int offset;
        int size;

        SubList(AbstractList<E> parent,
                int offset, int fromIndex, int toIndex) {
            this.parent = parent; // -----3⃣️
            this.parentOffset = fromIndex;
            this.offset = offset + fromIndex;
            this.size = toIndex - fromIndex;
            this.modCount = ArrayList.this.modCount;
        }
     
      	public void add(int index, E e) {
            rangeCheckForAdd(index);
            checkForComodification();
            parent.add(parentOffset + index, e); // parent.add调用的就是ArraryList.add方法 ---- 1⃣️
            this.modCount = parent.modCount;
            this.size++;
        }
  			private void checkForComodification() {
            if (ArrayList.this.modCount != this.modCount)
                throw new ConcurrentModificationException();
        }
     ...
   }
  
}

// AbstractList
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
  ...
    public boolean add(E e) {
        add(size(), e);
        return true;
    }
  
  public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }
  ...
}

LinkedList

具有Link的特点:

  • 快速插入
  • 查询慢

不是线程安全的, 因为内部维护了modCount变量, 此变量并没有使用volatile进行修饰, 因此当多个线程进行操作arrayList时, modCount的值将无法保证一致, 因此抛出ConcurrentModificationException异常(因为在进行读写之前均会对modCount进行校验)

类定义

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

重要的成员变量

protected transient int modCount = 0; // 记录对象被修改的次数
// 使用的数据结构
transient Node<E> first;
transient Node<E> last;

因为使用的是链表, 因此没有扩容一说; add remove 方法也都是链表的修改 简单没有什么特别需要讲解的