本篇主要介绍ArrayList的用法和源码分析,基于jdk1.8,先从List接口开始。java
List接口定义了以下方法:数组
int size(); boolean isEmpty(); boolean contains(Object o); Iterator<E> iterator(); Object[] toArray(); <T> T[] toArray(T[] a); boolean add(E e); void add(int index, E element); boolean addAll(Collection<? extends E> c); boolean addAll(int index, Collection<? extends E> c); boolean remove(Object o); E remove(int index); boolean removeAll(Collection<?> c); boolean retainAll(Collection<?> c); void clear(); E get(int index); E set(int index, E element); int indexOf(Object o); int lastIndexOf(Object o); List<E> subList(int fromIndex, int toIndex); ListIterator<E> listIterator(); ListIterator<E> listIterator(int index);
乍一看,这么多方法。其实不少方法是一样的功能,方法重载而已。
接下来逐个介绍下List定义的方法。dom
方法名言简意赅,基本上均可以从方法名知道方法的目的。
接下来分析List的经常使用实现类:
ArrayList
LinkedList
Vectoride
本篇介绍ArrayList源码分析
在个人理解中,ArrayList是一个封装的数组,提供了一些便利的方法供使用者使用,规避了使用原生数组的风险。性能
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
继承了AbstractList即成为了List,就要实现定义的全部方法。
实现了RandomAccess接口 就是提供了随机访问能力,能够经过下标得到指定元素
实现了Cloneable接口 表明是可克隆的,须要实现clone方法
实现了Serializable接口 表明ArrayList是可序列化的学习
下面介绍下ArrayList的主要方法ui
boolean add(E e)
先附上源码this
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
此方法的执行逻辑:spa
private void ensureCapacityInternal(int minCapacity) { //是不是空List,是则使用初始容量扩容 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; //增长修改次数 // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容1.5倍,采用位运算 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);//最大容量就是Integer的最大值 // minCapacity is usually close to size, so this is a win: //采用Arrays的copyOf进行深拷贝,其中调用的本地方法System.arraycopy,此方法是在内存中操做所以速度会很快。 elementData = Arrays.copyOf(elementData, newCapacity); //至此扩容结束 }
void add(int index, E element)方法稍有不一样
public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! //将index -> (size -1)的元素都日后移动一位 System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }
boolean addAll(Collection<? extends E> c)
boolean addAll(int index, Collection<? extends E> c)
这两个方法和上面的add大同小异,第一步都是判断容量,并扩容。
容量大小从1变为c的length,elementData[index] = element;赋值也变为数组拷贝,
直接上代码,秒懂。
public boolean addAll(Collection<? extends E> 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; } public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(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; }
E remove(int index)
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; // clear to let GC do its work return oldValue; }
boolean remove(Object o)
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; // clear to let GC do its work }
这种方法依赖元素的equals方法,循环遍历数组,由于ArrayList是容许null元素的,因此不管是使用if (o.equals(elementData[index])) 仍是 if (elementData[index].equals(o)) 都可能产生空指针,因此单独对null进行处理,逻辑都是同样的。
奇怪的是这个fastRemove方法,本来觉得会有些特殊处理,结果发现代码和上面remove(int index)中的如出一辙,为何上面的remove中不调用这个fastRemove呢?难道写两个remove方法的不是同一人?逻辑不影响,只是代码冗余了一点。
boolean removeAll(Collection<?> c)
public boolean removeAll(Collection<?> c) { Objects.requireNonNull(c); return batchRemove(c, false); }
删除存在于目标集合的方法,使用了batchRemove(Collection<?> c, boolean complement)这个方法,这个方法做了封装,在retainAll(Collection<?> c)取并集这个方法也有使用。
retainAll中是这样使用的:
return batchRemove(c, true);
和咱们的removeAll只差第二个参数boolean complement,remove是false,retail是true,那究竟是什么意思呢?complement的原意的补充,在这里我理解为保留,remove就不保留,retail就保留,接着咱们分析batchRemove这个方法。
private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; int r = 0, w = 0; boolean modified = false; try { for (; r < size; r++) if (c.contains(elementData[r]) == complement) elementData[w++] = elementData[r]; } finally { // Preserve behavioral compatibility with AbstractCollection, // even if c.contains() throws. if (r != size) { System.arraycopy(elementData, r, elementData, w, size - r); w += size - r; } if (w != size) { // clear to let GC do its work for (int i = w; i < size; i++) elementData[i] = null; modCount += size - w; size = w; modified = true; } } return modified; }
定义了 r 和 w两个int,r用于遍历原来的数组,w的意思是新的数组的size。
假如定义2个数组用于举例,
数组1,五个元素 1,2,3,4,5
数组2,五个元素 3,4,6,7,8
数组1调用removeAll(数组2)
此时进入batchRemove,咱们来一步一步走看r,w如何变化
此时complement是false,即数组2中没有数组1中第r个元素才知足if条件
首先执行
// Preserve behavioral compatibility with AbstractCollection, // even if c.contains() throws. if (r != size) { System.arraycopy(elementData, r, elementData, w, size - r); w += size - r; }
咱们的状况是 r=size,那何时r会不等于size呢,jdk中写了注释,就是在if判断时,调用数组2的contains方法,可能会抛空指针等异常。这时数组尚未遍历完,那r确定是小于size的。
那没判断的那些数据还要不要处理?保守起见jdk仍是会将他保存在数组中,由于最终w是做为新的size,因此w加上了没处理过的个数size - r。
接着执行下面一段
if (w != size) { // clear to let GC do its work for (int i = w; i < size; i++) elementData[i] = null; modCount += size - w; size = w; modified = true; }
这段代码意图很清晰,w由于是新的size值,因此将w及其以后的都置位null,增长修改次数,
给size赋予新值以后就结束了。
boolean contains(Object o)
contains 底层调用的是indexOf方法
public boolean contains(Object o) { return indexOf(o) >= 0; }
int indexOf(Object o)
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; }
就是循环遍历,一个个比对,有则返回对应下标,无则返回-1
对应的lastIndexOf是从最后往前遍历。
public int lastIndexOf(Object o) { if (o == null) { for (int i = size-1; i >= 0; i--) if (elementData[i]==null) return i; } else { for (int i = size-1; i >= 0; i--) if (o.equals(elementData[i])) return i; } return -1; }
E get(int index)
rangeCheck(index); return elementData(index);
先检查index,再返回数组对应元素。
E set(int index, E element)
public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
先检查index,在覆盖index的元素,返回旧元素。
void clear()
modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0;
增长修改次数,将全部元素置空,size设置为0,须要注意的是,数组的大小是没有变化的。
int size()
size方法就是直接将size变量直接返回
public int size() { return size; }
boolean isEmpty()
判断size是否等于0
public boolean isEmpty() { return size == 0; }
Iterator<E> iterator()
public Iterator<E> iterator() { return new Itr(); }
如代码所示,建立了一个Itr对象并返回,接下来看Itr类的定义
private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
首先介绍下三个成员变量
日常经常使用的迭代器方法
hasNext()
就是判断当前索引是否等于size。
E next()
首先检查list是否被修改过,expectedModCount是在建立对象时就得到了,若是在以后对list进行了其余修改操做的话,modCount就会增长,就会抛出ConcurrentModificationException。
没有异常就按下表返回坐标,cursor自增,lastRet也自增。
void remove()
首先判断是否能删除,若能则调用父类的remove方法,删除元素,接着会更新cursor和lastRet。
最重要的是会更新expectedModCount,此时调用了父类的remove方法,会使modCount+1,因此更新了 expectedModCount,让后续的检查不会抛异常。
ListIterator<E> listIterator
listIterator和iterator同样,只不过listIterator有更多的方法,至关于iterator的增强版。
定义以下
private class ListItr extends Itr implements ListIterator<E> { ListItr(int index) { cursor = index; } public boolean hasPrevious() { return cursor != 0; } public E previous() { checkForComodification(); try { int i = cursor - 1; E previous = get(i); lastRet = cursor = i; return previous; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } public int nextIndex() { return cursor; } public int previousIndex() { return cursor-1; } public void set(E e) { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.set(lastRet, e); expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } public void add(E e) { checkForComodification(); try { int i = cursor; AbstractList.this.add(i, e); lastRet = -1; cursor = i + 1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } }
没有什么须要注意的地方,基本的方法都从Iterator中继承过来并添加一些方法。
List<E> subList(int fromIndex, int toIndex)
这又是一个内部类
class SubList<E> extends AbstractList<E> { private final AbstractList<E> l; private final int offset; private int size; SubList(AbstractList<E> list, int fromIndex, int toIndex) { if (fromIndex < 0) throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); if (toIndex > list.size()) throw new IndexOutOfBoundsException("toIndex = " + toIndex); if (fromIndex > toIndex) throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); l = list; offset = fromIndex; size = toIndex - fromIndex; this.modCount = l.modCount; } 。 。 。 }
持有了父类的引用,内部的全部方法都是经过父类的这个引用去完成的,
因此须要注意的是,subList不是返回一个新的List,仍是原来的引用,因此改变subList的数据,原有的数据也会更改。
终于把基本的方法都介绍完了,从源码的角度分析了全部的方法,感受对ArrayList知根知底了,用它时确定会更加驾轻就熟了,看了源码才有原来实现都这么简单啊这样的感受,不过从中也学习到了大牛规范的代码风格,良好的结构,可读性很高。下篇分析List的另外一个实现类,LinkedList。