ArrayList详解
ArrayList
1.概述
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
- ArrayList是用数组实现的线性列表,其是相当与动态数组。
- ArrayList查找、修改速度较快,插入、删除速度较慢。
- ArrayList可以存放任何元素,包括null对象。
- ArrayList是线程不同步的。
ArrayList的类关系图如下:
2.详解
字段与构造函数
1 private static final int DEFAULT_CAPACITY = 10; // ArrayList默认容量 2 transient Object[] elementData; // ArrayList中存放元素的数组 3 private int size; // ArrayList存放元素的个数 4 /** 5 * 常量,代表ArrayList中没有元素。只有当使用有参的构造函数且initialCapatity为0时返回它。 6 */ 7 private static final Object[] EMPTY_ELEMENTDATA = {}; 8 /** 9 *常量,当使用无参构造函数时返回它。 10 */ 11 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 12 /** 13 * 默认的构造函数:创建容量为10的ArrayList 14 */ 15 public ArrayList(){ 16 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 17 } 18 /** 19 * 创建一个指定容量的ArrayList 20 * @param initialCapatity ArrayList的容量 21 */ 22 public ArrayList(int initialCapatity){ 23 if(initialCapatity > 0) { 24 this.elementData = new Object[initialCapatity]; 25 }else if(initialCapatity == 0) { 26 this.elementData = EMPTY_ELEMENTDATA; 27 }else { 28 throw new IllegalArgumentException("Illegal Capatity" + inintCapatity); 29 } 30 } 31 /** 32 * 创建一个包含Collection的ArrayList 33 * @param @param c Collection 34 */ 35 public ArrayList(Collection<? extends E> c){ 36 elementData = c.toArray(); 37 if((size = elementData.length) != 0){ 38 // c.toArray might (incorrectly) not return Object[] (see 6260652) 39 if(elementData.getClass() != Object[].class){ 40 elementData = Arrays.copyof(elementData, size, Object[].class); 41 } 42 } 43 }
一些方法
1 public E get(int index){ 2 // 检查索引位置 3 rangeCheck(index); 4 return elementData[index]; 5 } 6 public E set(int index, E element){ 7 // 检查索引位置 8 rangeCheck(index); 9 E oldValue = elementData[index]; 10 elementData[index] = element; 11 return oldValue; 12 } 13 public boolean add(E e){ 14 // 确保ArrayList有足够的容量:验证+扩容 15 ensureCapacityInternal(size + 1); 16 elementData[size++] = e; 17 return true; 18 } 19 public void add(int index, E element){ 20 // 检查索引是否合适 21 rangeCheckForAdd(index); 22 // 确保ArrayList有足够的容量:验证+扩容 23 ensureCapatityInternal(size + 1); 24 // 把原数组从index后的每一个元素向后移一位 25 System.arraycopy(elementData,index,elementData,index+1,size-index); 26 elementData[index] = element; 27 size++; 28 } 29 public E remove(int index){ 30 // 检查索引位置是否合适 31 rangeCheck(index); 32 modCount++; 33 E oldValue = elementData(index); 34 // 把原数组从index后的每一个元素向前移动一位 35 int numMoved = size - index - 1; 36 if(numMoved > 0){ 37 System.arraycopy(elementData, index+1, elementData, index, numMoved); 38 } 39 elementData[--size] = null; //clear to let GC do its work 40 return oldValue; 41 }
扩容
在上面add()方法中我们都需要验证ArrayList容量是否足够,不足需要扩容。
1 /** 2 * 验证+扩容 3 * @param minCapacity 现在ArrayList存放元素所需要的最小容量 4 */ 5 private void ensureCapacityInternal(int minCapacity) { 6 if(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { 7 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 8 } 9 ensureExplicitCapacity(minCapacity); 10 } 11 private void ensureExplicitCapacity(int minCapacity) { 12 modCount++; 13 if(minCapacity - elementData.length > 0){ 14 // 扩容 15 grow(minCapacity); 16 } 17 } 18 /** 19 * 该方法是扩容:扩大至原来的1.5倍,我没有看懂怎么就扩大到原来的1.5倍~(0)~ 20 */ 21 private void grow(int minCapacity) { 22 // 扩大至原来的1.5倍 23 int oldCapacity = elementData.length; 24 int newCapacity = oldCapacity + (oldCapacity >> 1); 25 if(newCapacity - minCapacity < 0){ 26 newCapacity = minCapacity; 27 } 28 if(newCapacity - MAX_ARRAY_SIZE > 0){ 29 newCapacity = hugeCapacity(minCapacity); 30 } 31 // elementData扩容 32 elementData = Arrays.copyOf(elementData,newCapacity); 33 }
以上是部分源码分析。
还有几点:
- 以上是JDK1.8的源码,其中怎么就扩大要原来的1.5倍,我没有看懂。而且为什么要扩大要原来的1.5倍我也不知道。之前版本是增加至(oldCapacity * 3)/2 + 1。
- 从上面我们可以看出在插入、删除元素时我们需要调用System.arraycopy()方法,这就比较耗时,所以说ArrayList在插入、删除时的速度慢的原因。当然这是由于ArrayList底层使用数组这种数据结构的缘故。
3.自己模仿写的ArrayList
class ArrayList<E> extends AbstractList<E> implements List<E> { // 默认容量 private static final int DEFAULT_CAPACITY = 10; // 存放元素数量 private int size; // 存放元素的数组 private Object[] elementData; ArrayList(){ elementData = new Object[DEFAULT_CAPACITY]; } ArrayList(int initCapacity){ if(initCapacity > 0){ elementData = new Object[initCapacity]; }else { throw new IllegalArgumentException("Illegal Capacity" + initCapacity); } } ArrayList(Collection< ? extends E> c){ elementData = c.toArray(); this.size = elementData.length; if(size != 0){ if(elementData.getClass() != Object[].class){ elementData = copyOf(elementData,size,Object[].class); } } } @Override @SuppressWarnings("unchecked") public E get(int index) { check(index); return (E) elementData[index]; } @Override public int size() { return size; } @Override public boolean add(E e) { ensureCapacity(size + 1); elementData[size] = e; size++; return true; } @Override public void add(int index, E element) { check(index); ensureCapacity(size + 1); System.arraycopy(elementData,index,elementData,index+1,size - index); elementData[index] = element; size++; } @Override public boolean remove(Object o) { for(int index = 0; index < size; index++){ if(Objects.equals(o,elementData[index])){ int numMoved = size - index - 1; if(numMoved > 0){ System.arraycopy(elementData, index + 1, elementData, index, numMoved); } elementData[size] = null; size--; return true; } } return false; } @Override @SuppressWarnings("unchecked") public E remove(int index) { check(index); E oldValue = (E) elementData[index]; int numMoved = size - index - 1; if(numMoved > 0){ System.arraycopy(elementData, index + 1, elementData, index, numMoved); } elementData[size] = null; size--; return oldValue; } @Override @SuppressWarnings("unchecked") public E set(int index, E element) { check(index); E oldValue = (E) elementData[index]; elementData[index] = element; return oldValue; } // 验证索引 private void check(int index){ if(index < 0 || index > size){ throw new IndexOutOfBoundsException("Index:" + index + "Size:" + size); } } // 验证+扩容 private void ensureCapacity(int minCapacity){ // 验证 if(minCapacity > elementData.length){ // 扩容 int oldCapacity = elementData.length; int newCapacity = (int) (oldCapacity * 1.5); elementData = Arrays.copyOf(elementData, newCapacity); } } }