本文共 8720 字,大约阅读时间需要 29 分钟。
因为在看其他文章的时候,其中提到ArrayList其实就是对数组的一些操作细节封装起来,对集合的操作,实际上是对里面的数组进行操作,相比较于数组,ArrayList可以动态扩容的。所以就引起了自己去查看ArrayList的源码实现。
简单的分析下主要几个方法的实现过程
//默认的数组的容量 private static final int DEFAULT_CAPACITY = 10; // initialCapacity为0的实例(new ArrayList(0))对应的空的数组实例 private static final Object[] EMPTY_ELEMENTDATA = {}; //通过不设置数组容量(new ArrayList())的实例对应的空数组实例, private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //用来存放元素的数组 transient Object[] elementData; //集合中元素的数量 private int size;
我们可以看到其实最终 加入到ArrayList集合中的元素都存放到了Object[] elementData中,并且ArrayList的默认的容量为10。
注意elementData采用transient修改表示该字段不被序列化。
我们可以通过三种方式来创建一个ArrayList实例。
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
就是初始化一个空的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); } }
就是初始化一个initialCapacity大小的elementData数组或着一个空的elementData数组
public ArrayList(Collection 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; } }
根据传入的集合来初始化elementData数组。
直接在集合元素依次累加元素
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
从源码中可以看到,其实就是给对应的size 的位置的elementData数组元素进行赋值。当然在赋值之前,会通过ensureCapacityInternal()来确定elementData数组大小是否满足要求,从而创建出一个可用的elementData数组来存放元素。重点看下ensureCapacityInternal()这里面的一些源码
private void ensureCapacityInternal(int minCapacity) {//如果通过new ArrayList这种方式创建的ArrayList,则取得minCapacity和DEFAULT_CAPACITY的最小值 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity);}
从源码中可以看到如果通过new ArrayList这种方式创建的ArrayList,则取得minCapacity和DEFAULT_CAPACITY的最大值。还没有添加元素之前size为0,minCapacity为1,而DEFAULT_CAPACITY为10,那么最后minCapacity的值为10,随着size的累加,会逐渐minCapacity变成size的大小。
private void ensureExplicitCapacity(int minCapacity) {//集合被修改的次数 modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
判断集合中的元素和elementData的数组长度。如果集合中的元素超出elementData容量时,则通过grow()进行扩容;否则直接返回。
分析下上面三种构造函数创建的ArrayList的第一次调用add()情况:
(a)new ArrayList()
此时minCapacity为10,elementData.length为0,那么需要调用grow()进行扩容
(b) new ArrayList(int initialCapacity)
此时minCapacity为1,initialCapacity=0时,elementData.length为0,那么需要调用grow()进行扩容;如果initialCapacity>1,则直接返回即可。
(c)new ArrayList(Collection<? extends E> c)
此时minCapacity为c.length+1,elementData.length也为传入c.length,此时需要调用grow()进行扩容。
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 int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
通过oldCapacity + (oldCapacity >> 1);来获取 newCapacity,即1.5倍的oldCapacity。通过分析各种情况,对elementData的数组进行复制扩容。直到超过最大值。
得到能够存放元素的数组之后,直接在数组后面进行添加元素即可。
数组直接赋值即可。
在特定位置进行添加元素
public void add(int index, E element) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }
同样首先会先确定 elementData数组中足以放下该元素
然后调用System的arrayCopy()将elementData起始index位置的元素复制到elementData元素的index+1的位置开始存放,一共复制size-index个元素。
最后进行赋值即可。
替换特定位置的元素。
public E set(int index, E element) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); E oldValue = (E) elementData[index]; elementData[index] = element; return oldValue; }
从源码中可以看到,其实就是直接将 elementData数组中的元素对应位置进行修改,同时将该位置的原值返回。
添加集合。
public boolean addAll(Collection c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; }
原理和添加单个元素是一样的,唯一的区别就是在size累加的大小由1变成了添加集合的length。通过System.arraycopy()将a从0位置的元素复制到elementData数组的从size开始的后面,一共复制a.length大小的元素。
指定位置添加集合
public boolean addAll(int index, Collection c) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount//判断要插入的位置,将后面的位置后移 int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a, 0, elementData, index, numNew); size += numNew; return numNew != 0; }
同样确保elementData的容量是足够的,然后会判断index的位置 ,如果在中间,首先需要System.arraycopy()将elementData从index位置之后的元素开始复制到elementData的index+c.length的位置之后,共复制size-index个元素(即index后面的所有元素)。
取出给定位置的元素。
public E get(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); return (E) elementData[index]; }
很简单直接读取 elementData对应位置的元素
查找object对应的索引值
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; }
直接通过循环进行匹配object,找到对应在 elementData数组中的位置。
移除指定位置的元素
public E remove(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); modCount++; E oldValue = (E) 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; }
从 elementData读取元素,并将elementData的对应index+1之后的元素复制到index位置,同时将elementData的存储空间减1,返回对应位置的元素。
将指定的object对象给移除
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; }
与上面1)不同的是,首先要找到该object对应的index,然后从elementData中删除的理论通1)。从上面也可以看到ArrayList中支持null。
清空所有的元素
public void clear() { modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0; }
就是直接将 elementData中的所有元素置为null即可。
从源码读下来,发现ArrayList的一些操作就是在操作里面的一个数组,其实就是对数组的操作,只不过会根据加入的元素个数动态对数组进行扩容。
转载地址:http://kpejz.baihongyu.com/