ArrayList是咱们很是经常使用的一个集合,那么ArrayList是如何实现呢?java
ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("Hello"); if (arrayList.contains("Hello")) { System.out.println("[Hello] is in ArrayList!"); } arrayList.remove("Hello");
public ArrayList() { private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //存储数据的集合 transient Object[] elementData; // non-private to simplify nested class access //.... this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
从构造函数能够看出,如今咱们的集合是一个空集合,真正存储数据的数组也是一个长度为0的数组。数组
public boolean add(E e) { modCount++; add(e, elementData, size); return true; } private void add(E e, Object[] elementData, int s) { //存储数据的数组已经所有被使用 if (s == elementData.length) //扩张数组 elementData = grow(); elementData[s] = e; size = s + 1; }
一个参数的add方法调用了private的add方法,在该方法中真正完成了向集合中添加元素。在添加元素的时候,若是size==存储数据的数组的长度,那么就代表数组已经存满了,这时候就须要将本来的数组进行扩张,如何扩张数组是经过 grow
方法来实现的。函数
private Object[] grow() { return grow(size + 1); } private Object[] grow(int minCapacity) { //复制当前数组到一个新的数组中(新的数组长度已经扩张过) return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity)); } //计算新的扩张长度 private int newCapacity(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; //计算新的容量 //新容量= 数组的长度+数组的长度向右位移1(例如:10>>1 = 5, 11>>1 = 5) //这种新容量的计算方式代表:当集合的长度越大时,集合每一次的扩张的幅度就会愈来愈大 int newCapacity = oldCapacity + (oldCapacity >> 1); //当新容量小于最小容量时,以最小容量为基准 if (newCapacity - minCapacity <= 0) { //当数组中没有元素的时候,会执行这个分支 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) //返回最小容量和默认容量中较大的一个 return Math.max(DEFAULT_CAPACITY, minCapacity); if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return minCapacity; } return (newCapacity - MAX_ARRAY_SIZE <= 0) ? newCapacity : hugeCapacity(minCapacity);//极端状况,能够不考虑(几乎不会走到这个分支) } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
Grow 方法是实现ArrayList长度可变的核心,其实现思想是源码分析
当数组存放满了的时候,就扩张数组(当集合中的元素的数量越多的时候,扩张的幅度就越大。)
而扩张数组是经过,创建新的数组(长度等于扩张后的长度),而后将旧的数组中的元素填充到新数组中,这种方式来实现的。this
Contains也是ArrayList集合很是经常使用的一个方法,用来判断集合中是否包含指定的元素。code
public boolean contains(Object o) { return indexOf(o) >= 0; } public int indexOf(Object o) { return indexOfRange(o, 0, size); } int indexOfRange(Object o, int start, int end) { Object[] es = elementData; if (o == null) { for (int i = start; i < end; i++) { if (es[i] == null) { return i; } } } else { for (int i = start; i < end; i++) { if (o.equals(es[i])) { return i; } } } return -1; }
contains方法最终调用了调用了indexOfRange方法,indexOfRange作的工做是,在集合指定的范围内判断是否包含指定的元素,若是包含就返回下标,不然就返回-1.ci
public boolean remove(Object o) { final Object[] es = elementData; final int size = this.size; int i = 0; //搜寻指定元素 found: { if (o == null) { for (; i < size; i++) if (es[i] == null) break found; } else { for (; i < size; i++) if (o.equals(es[i])) break found; } return false; } //移除元素 fastRemove(es, i); return true; } private void fastRemove(Object[] es, int i) { modCount++; final int newSize; //若是不是在数组的末尾移除元素 if ((newSize = size - 1) > i) //将i以后全部元素向前移动一个下标 System.arraycopy(es, i + 1, es, i, newSize - i); es[size = newSize] = null; }
remove方法的逻辑很清晰:element
ArrayList经过数组实现了可变集合,可是咱们从源码中能够看出来,若是要增长删除元素的话,是很是消耗资源和时间的(由于在频繁的操做数组),可是访问的时候是特别的快的(直接经过下标访问便可)。资源
能够得出结论: ArrayList不适合频繁的增长删除,可是适合查询。rem