目录
深入探讨源码之ArrayList
ArrayList
类图ArrayList
的数据结构ArrayList
的关键属性ArrayList
构造方法ArrayList
常用方法add方法ArrayList
中的fast-fail
机制add(i,o)方法set(i,o)方法get(i)方法remove(index)方法remove(Object)方法clear方法indexOf(o)
方法
深入探讨源码之ArrayList
java.util.ArrayList
位于JDK
的rt.jar中;出生于JDK1.2
。本文JDK
版本基于JDK1.8
。
List接口的可调整大小的数组实现,实现了所有可选的List操作,允许所有数据都为null。类Vector类似,ArrayList
是线程不安全的,Vector是线程安全的。
ArrayList
类图
上面这张图基本上描述的很清晰了,实现了四个接口一个抽象类。它继承了AbstractList
抽象类,实现了List、RandomAccess
, Cloneable
,Serializable
接口。
它继承于
AbstractList
,实现了List
,RandomAccess
随机访问,Cloneable
可克隆,java.io.Serializable
序列化]这些接口。ArrayList
继承了AbstractList
,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。ArrayList
实现了RandmoAccess
接口,即提供了随机访问功能。ArrayList
实现了Cloneable
接口,即覆盖了函数clone(),能被克隆。ArrayList
实现java.io.Serializable
接口,这意味着ArrayList
支持序列化,能通过序列化去传输。和Vector不同,
ArrayList
中的操作不是线程安全的。所以,建议在单线程中才使用ArrayList
,而在多线程中可以选择Vector或者CopyOnWriteArrayList
。
ArrayList
的数据结构
数据结构往往是它的灵魂所在,理解底层的数据结构其实就理解了该类的实现思路,具体的实现细节再具体分析。ArrayList
的数据结构是:
说明:底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据。我们对ArrayList
类的实例的所有的操作底层都是基于数组的。
ArrayList
的关键属性
//初始化大小为10private static final int DEFAULT_CAPACITY = 10;//空对象数组private static final Object[] EMPTY_ELEMENTDATA = {};//缺省空对象数组private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//存数据的数组transient Object[] elementData;//当前ArrayList的大小private int size;//ArrayList最大容量//Integer.MAX_VALU=(2的31次方)-1private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
ArrayList
构造方法
//开发人员使用最多的构造方法,全部使用默认参数public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_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); }}public ArrayList(Collection<? extends E> 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; }}
ArrayList
常用方法
add方法
往ArrayList
中添加元素,使用demo
public class ArrayListDemo { public static void main(String[] args) { ArrayList<String> arrayList=new ArrayList<>(); arrayList.add("Java后端技术栈"); arrayList.add("tian"); arrayList.add("Java"); arrayList.add("DB"); Iterator<String> mIterator = arrayList.iterator(); while (mIterator.hasNext()){ if ("tian".equals(mIterator.next())){ arrayList.add("C"); } } }}
add方法源码分析
public boolean add(E e) { //确保内部容量,size为elementData的长度,本次添加一个 //即size+1 //使用第一个构造方法,第一次添加数据的时候,size=0 ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true;}private void ensureCapacityInternal(int minCapacity) { //elementData是空数组,minCapacity=0+1=1 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}private static int calculateCapacity(Object[] elementData, int minCapacity) { //两个数组都是空的,所以这里是相等的 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //返回DEFAULT_CAPACITY=10和minCapacity=1比较,谁大返回谁 //那么这里就返回10 return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity;}private void ensureExplicitCapacity(int minCapacity) { //用来标记当前arraylist集合操作变化的次数 modCount++; //如果minCapacity>elementData.length //minCapacity=10,elementData.length=0 if (minCapacity - elementData.length > 0){ grow(minCapacity); }}private void grow(int minCapacity) { //将扩充前的elementData大小给oldCapacity int oldCapacity = elementData.length; //newCapacity就是1.5倍的oldCapacity //第一次添加元素的时候,newCapacity依旧是0 int newCapacity = oldCapacity + (oldCapacity >> 1); //这句话就是适应于elementData就空数组的时候,length=0, //那么oldCapacity=0,newCapacity=0,所以这个判断成立, //在这里就是真正的初始化elementData的大小了, //就是为10.前面的工作都是准备工作。 if (newCapacity - minCapacity < 0){ newCapacity = minCapacity; } //如果newCapacity超过了最大的容量限制,就调用hugeCapacity, //也就是将能给的最大值给newCapacity //比较一下newCapacity与(2的31次方)-1谁大 if (newCapacity - MAX_ARRAY_SIZE > 0){ newCapacity = hugeCapacity(minCapacity); } // minCapacity is usually close to size, so this is a win: //新的容量大小已经确定好了,就copy数组,改变容量大小。 elementData = Arrays.copyOf(elementData, newCapacity);}//OOM或者就是赋值最大容量private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); //如果minCapacity都大于MAX_ARRAY_SIZE,那么就Integer.MAX_VALUE返回, //反之将MAX_ARRAY_SIZE返回。因为maxCapacity是三倍的minCapacity, //可能扩充的太大了,就用minCapacity来判断了。 //Integer.MAX_VALUE:2147483647 MAX_ARRAY_SIZE:2147483639 //也就是说最大也就能给到第一个数值。还是超过了这个限制, //就要溢出了。相当于arraylist给了两层防护。 return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;}
ArrayList
中的fast-fail
机制
运行上面demo
还记得上面源码中ensureExplicitCapacity
方法中有个modCount
变量么?
抛出的异常正是我们刚才提到的ConcurrentModificationException
并发修改异常,正是由于fail-fast机制导致的。在什么情况下会出现该异常呢?我先简单说下,一般情况下有两种情况,一种情况发生在多线程操作,当a线程正在通过迭代器操作集合arrayList
时,同时b线程对arrayList
进行添加或者删除元素,会触发fail-fast机制,抛出该异常。另一种情况是在迭代集合arrayList
的过程中对arrayList
集合进行元素添加或者删除操作时,会触发fail-fast机制,抛出该异常。归根结底就是当我们进行集合数据迭代时,有其他操作修改了当前Arraylist
的modCount
的值,导致modCount != expectedModCount
,会抛出该异常。
public Iterator<E> iterator() { return new Itr();}private class Itr implements Iterator<E> { // next 元素角标 int cursor; // index of next element to return // last 元素角标 int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; 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]; } final void checkForComodification() { //当modCount != expectedModCount时, //会抛出ConcurrentModificationException异常 if (modCount != expectedModCount) throw new ConcurrentModificationException(); }}
add、remove、clear元素都会使得modCount
变化。
add(i,o)方法
添加到指定的位置。
使用demo
public class ArrayListDemo1 { public static void main(String[] args) { ArrayList<String> arrayList=new ArrayList<>(); arrayList.add(1,"Java后端技术栈"); }}
具体源码
//index为即将要存放数据的位置,element为数据public void add(int index, E element) { //添加之前,先检查index是否满足条件 rangeCheckForAdd(index); //前面已经分析了 ensureCapacityInternal(size + 1); // Increments modCount!! //将index及其后边的所有的元素整块后移,空出index位置 System.arraycopy(elementData, index, elementData, index + 1, size - index); //将元素存放在index位置上 elementData[index] = element; //数组大小加1 size++;}private void rangeCheckForAdd(int index) { //如果使用第一种构造方法,那么size==0 //如果index>size或者index<0就会抛数组越界异常 if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}
运行demo,这个方法慎用,平时工作使用的不是很多,因为在使用的时候,需要保证不满足
index > size和Index<0中的任何一个条件,否则将出现:
set(i,o)方法
使用demo
public class ArrayListDemo1 { public static void main(String[] args) { ArrayList<String> arrayList=new ArrayList<>(); arrayList.add("Java后端技术栈"); arrayList.add("tian"); arrayList.add("Java"); arrayList.set(1,"C"); for (String string:arrayList){ System.out.println(string); } }}
源码
//把Index位置上的旧值拿出来,然后把新值放进去public E set(int index, E element) { rangeCheck(index); //获取index位置上的旧值 E oldValue = elementData(index); //把index位置上设置新值 elementData[index] = element; //返回旧值 return oldValue;}//index大于ArrayList的大小,就明显是数组越界了。private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}
get(i)方法
获取数据
//检查index是否越界,然后获取index上的数据public E get(int index) { rangeCheck(index); return elementData(index);}E elementData(int index) { return (E) elementData[index];}
remove(index)方法
public E remove(int index) { //范围检查 rangeCheck(index); //modCount加1 modCount++; //取出旧值 E oldValue = elementData(index); //获取从哪个下标开始移动数据 int numMoved = size - index - 1; //如果删除的不是最后一个元素 if (numMoved > 0) //删除的元素到最后的元素整块前移 System.arraycopy(elementData, index+1, elementData, index, numMoved); //将最后一个元素设为null,方便垃圾回收集在下次gc的时候就会回收掉了 elementData[--size] = null; // clear to let GC do its work return oldValue; }
remove(Object)方法
和remove(index)雷士,只是多了一层遍历而已。性能明细没有remove(index)好。
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; } //和前面remove(index) 类似了 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 }
clear方法
public void clear() { modCount++; // clear to let GC do its work //清楚就是把ArrayList中的所有对象置为null,方便垃圾收集器收集 for (int i = 0; i < size; i++) elementData[i] = null; //最后把size置为0 size = 0;}
indexOf(o)
方法
获取o所在的下标,如果ArrayList中没有o,那么就返回 -1;
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; }