概述 ArrayList是常用的数据结构,在java8中,其主要是通过数组来实现的。在插入一个元素时会先判断数组容量是否还能放置元素,如果不能,则进行扩容。本文主要分析add、get、set、remove方法。
一些成员变量 1 2 3 4 5 6 7 8 9 10 11 private static final int DEFAULT_CAPACITY = 10 ;private static final Object[] EMPTY_ELEMENTDATA = {};transient Object[] elementData; private int size;
add方法 add(E e)方法 大致流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 start=>start: Start isArrayEmpty=>condition: 数组是否为空 reCalcMinCapacity=>operation: 重新计算minCapacity 大小,取 DEFAULT_CAPACITY 和minCapacity 的最大值 isResize=>condition: minCapacity大于 当前数组大小 resize=>operation: 扩容数组为原先的1.5倍 addObj=>operation: 往数组末尾中加入元素 end=>end: End start->isArrayEmpty(yes)->reCalcMinCapacity->isResize(yes)->resize->addObj->end isArrayEmpty(no)->isResize(no)->addObj
代码 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 public boolean add (E e) { ensureCapacityInternal(size + 1 ); elementData[size++] = e; return true ; } private void ensureCapacityInternal (int minCapacity) { if (elementData == EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(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); }
add(int index, E e)方法 这个方法与add(E e)
方法类似,只不过在函数刚开始的地方多了个边界检查,在确保数组能够放置新元素后面增加了下标元素及其后面的元素向后移动一位的操作。
1 2 3 4 5 6 7 8 9 10 11 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++; }
get方法 get方法很简单,就是检查下标的边界,然后直接取数组下标的元素返回。
1 2 3 4 5 6 7 8 9 10 11 12 public E get (int index) { rangeCheck(index); return elementData(index); } @SuppressWarnings("unchecked") E elementData (int index) { return (E) elementData[index]; }
set方法 set方法也很简单,也是先检查下标的边界,然后先取出原有下标的元素,再直接替换新的元素,最后将原先的元素返回。
1 2 3 4 5 6 7 8 9 public E set (int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
remove方法 remove(int index)方法 根据下标删除元素,首先进行边界检查,然后取出下标对应的元素,再将这个元素后面的元素都向前移动一位,接着将数组最后一个元素置空以便垃圾回收器能够回收该对象,最后将下标指向的原先的对象返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public E remove (int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1 ; if (numMoved > 0 ) System.arraycopy(elementData, index+1 , elementData, index, numMoved); elementData[--size] = null ; return oldValue; }
remove(Object o)方法 根据对象删除对象的方法主要是根据对象是否是空的进行分别处理。
删除的对象是null
,则查找数组中的元素,如果有null
元素,则将第一个null
元素删除
删除的对象不是null,则根据equals
方法将对象与数组中的元素进行比较,将第一个相等的元素删除
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 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 ; } private void fastRemove (int index) { modCount++; int numMoved = size - index - 1 ; if (numMoved > 0 ) System.arraycopy(elementData, index+1 , elementData, index, numMoved); elementData[--size] = null ; }
其他
ArrayList允许增加null
对象
ArrayList允许添加重复对象
ArrayList对象是有序的,新增的对象在尾部
ArrayList不是线程安全的,需要在外部调用的地方手动保证线程安全。因为在add函数中有size++的操作,当有两个线程同时调用add函数时,假设A线程已经在对应的位置上放置了元素,但是size还未加一,此时切换到B线程执行add函数直到结束,size加一了,切回A线程执行size++,此时A线程中的size值还是原先的没有经过B线程加一的值,在A线程执行完size++后,此时size只会加一而不会加二,从而导致数据丢失。