List接口 – 允许保存重复数据 <--more-->--more-->
ArrayList,Vector,LinkedList
每种集合的通用逻辑,都被抽象到相应的抽象类中,AbstractList集中了各种List操作的通用部分。这些集合不是完全孤立的,LinkList本身既是List,也是Deque
ArrayList ArrayList类 通过extends关键字继承AbstractList抽象类,通过关键字implement实现List集合接口,RandomAccess标记接口,Cloneable克隆接口,Serializable序列化接口
1 2 public class ArrayList <E > extends AbstractList <E > implements List <E >, RandomAccess , Cloneable , java .io .Serializable
接口RandomAccessa.java(实现随机访问)
此接口的主要目的是允许一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能。
接口RandomAccess 是一个标记接口,实现该接口的集合List 支持快速随机访问。List 集合尽量要实RandomAccess 接口,如果集合类是RandomAccess 的实现,则尽量用for(int i = 0; i < size; i++) 来遍历效率高,而不要用Iterator迭代器来遍历(如果List是Sequence List,则最好用迭代器来进行迭代)
接口Cloneable
接口Serializable
源码分析:
ArrayList的构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 private static final long serialVersionUID = 8683452581122892189L ;private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; transient Object[] 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); } } public ArrayList () { this .elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } public ArrayList (Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0 ) { if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { this .elementData = EMPTY_ELEMENTDATA; } }
无参构造方法中直接初始化Object[] elementData={};
带参构造方法是new Object()或者通过Arrays的方法转化为数组对象
问题一:ArrayList是数组还是集合
ArrayList实现List接口,重写其中的add(),get(),remove()等方法,而方法的内部代码是基于数组来处理数据的。
ArrayList是实现List接口的集合,是基于数组的集合,数据的存储容器是数组,集合方法是通过数组实现的(例如:泛型参数构造方法是将传入的集合c先转化为数组在进行处理的)。
通关add方法查看初始容量
ArrayList中定义了一个默认存储容器大小DEFAULT_CAPACITY=10,我们发现在构造方法创建ArrayList是并没有使用到该常量。在add添加元素的时候,先将size+1传入到ensureCapacityInternal()判断当前数组elementData是否需要扩容,足够size自增1,直接添加元素即可。
扩容:当数组还是默认的空数组时,这里的size+1=0 < DEFAULT_CAPACITY 会将容器大小扩容为10,此时的elementData扩容为Object[10]
问题二: ArrayList的初始容量是多少?
当调用构造方法创建ArrayList时,默认无参构造对象的容器初始大小为0。而当调用add方法时才会将数组扩容为10.
ArrayList的扩容问题:
当elementData.length < size+1时才会进行扩容,调用grow方法。
先取到当前容器大小oldCapacity,进行扩容,判断扩容容量是否合法,是否溢出,然后处理为合理的大小
使用newCapacity = oldCapacity + (oldCapacity >> 1)
计算出来预计扩容的大小,如果该大小仍然小于数组所扩容后的大小,newCapacity = minCapacity;
重新改写新扩容后数组容量,如果新数组大小大于数组的最大容量限度,调用huneCapacity()方法,直接将容量大小定义为Integer的最大值,最后调用Arrays方法进行拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 -------------------------------------------------------------------- private static final int DEFAULT_CAPACITY = 10 ; private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 ; private int size; public int size () { return size; } public boolean isEmpty () { return size == 0 ; } public boolean add (E e) { ensureCapacityInternal(size + 1 ); elementData[size++] = e; return true ; } private void ensureCapacityInternal (int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity (Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity (int minCapacity) { modCount++; if (minCapacity - elementData.length > 0 ) grow(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 int hugeCapacity (int minCapacity) { if (minCapacity < 0 ) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } public void add (int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1 ); System.arraycopy(elementData, index, elementData, index + 1 , size - index); elementData[index] = element; size++; }
ArrayList的序列化问题补充
集合的存储容器elementData 使用transient 关键字修饰不支持序列化,但是我们知道ArrayList 是支持序列化的,那我们是怎么序列化集合中的数据呢,这里不直接序列化elementData,而是遍历每个数据分别进行IO 流处理来实现存储容器中对象的序列化的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protected transient int modCount = 0 ; private void writeObject (java.io.ObjectOutputStream s) throws java.io.IOException { int expectedModCount = modCount; s.defaultWriteObject(); s.writeInt(size); for (int i=0 ; i<size; i++) { s.writeObject(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }
modCount是表示ArrayList对象被修改的次数,Iterator中有一个成员变量expectedModCount,在创建时会将modCount赋值给他,用来检验在迭代过程中List对象是否被修改,如果被修改就会抛出ConcurrentModificationException异常。在每次调用Iterator对象的next方法时都会调用checkForComodification()方法进行检验,将modCount与expectedModCount进行对比。