使用一个容器固然要从容器的构造开始,ArrayList重载了三种构造函数html
平常中最常使用的是无参数构造函数,使用另外一个ArrayList来构造新的ArrayList在诸如回溯算法中也很常见。java
public ArrayList() public ArrayList(int initialCapacity) public ArrayList(Collection<? extends E> c)
无参构造函数中将elementData
赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA
(即空数组),其中elementData
就是ArrayList存放元素的真实位置。也能够在初始化时将容器容量肯定为传入的int参数。算法
//类中定义的变量 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; transient Object[] elementData; // non-private to simplify nested class access,若是是私有变量,在内部类中获取会比较麻烦 //无参构造 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); } }
toArray()
方法实现可能不一样,返回值不必定为Object[]
,即elementData
的类型会发生变化(例子见ClassTypeTest.java)。因此须要进行类型判断,若elementData.getClass() != Object[].class
则使用Arrays工具类中的copyOf方法将elementData
的类型改回。public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // defend against c.toArray (incorrectly) not returning Object[] // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
//ClassTypeTest.java public class ClassTypeTest { public class Person{ } public class Student extends Person{ } public static void main(String[] args) { Person[] p = new Student[5]; System.out.println(p.getClass()); } } //output: //class [LClassTypeTest$Student;
容器的本质无非是替咱们保管一些咱们须要储存的数据(基本数据类型、对象),咱们能够往容器里加入,也能够从容器里获取,也能够删除容器内元素。使用容器而不是数组是由于数组对于咱们使用来讲过于不便利数组
ArrayList容器底层是基于数组实现,可是咱们使用的时候却不须要关心数组越界的问题,是由于ArrayList实现了数组的动态扩容,从add方法出发查看ArrayList是怎么实现的安全
elementData
数组扩容操做开始于 add方法,当grow()返回扩容后的数组,add方法在这个数组上进行添加(插入)操做。在add方法中看到的modCount变量涉及 Java 的 fail-fast 机制,将在本文后面进行讲解//size是ArrayList实际添加的元素的数量,elementData.length为ArrayList能最多容纳多少元素的容量 //经过代码能够看出,当size==elementData.length时,容器没法再放入元素,因此此时须要一个新的、更大的elementData数组 private int size; 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; }
minCapacity
个元素(即容量比原来至少大minCapacity
)private static final int DEFAULT_CAPACITY = 10; private Object[] grow() { return grow(size + 1); } private Object[] grow(int minCapacity) { int oldCapacity = elementData.length; if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { int newCapacity = ArraysSupport.newLength(oldCapacity, minCapacity - oldCapacity, /* minimum growth */ oldCapacity >> 1 /* preferred growth */); return elementData = Arrays.copyOf(elementData, newCapacity); } else { // 当oldCapacity==0 || elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时进入该分支 // 即容器使用无参构造函数 或 new ArrayList(0)等状况时进入 // elementData数组大小被扩容为 10 return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)]; } }
prefGrowth=oldCapacity/2
,由此处可看出大部分状况下扩容后的数组大小为原数组的1.5倍
newLength - MAX_ARRAY_LENGTH <= 0
进行判断,不能使用 newLength <= MAX_ARRAY_LENGTH
进行判断,若是 newLength
超过 2147483647 ,会溢出为负值,此时newLength
依旧小于MAX_ARRAY_LENGTH
。而用newLength - MAX_ARRAY_LENGTH <= 0
则是至关于将newLength
这个数字在“int环”上向左移动了MAX_ARRAY_LENGTH
位,若这个数字此时为负数(即落在绿色区域),则直接返回当前newLength
,不然进入hugeLength方法。public static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8; public static int newLength(int oldLength, int minGrowth, int prefGrowth) { // assert oldLength >= 0 // assert minGrowth > 0 int newLength = Math.max(minGrowth, prefGrowth) + oldLength; //!!! 判断数组大小是否超过int值容许的大小 if (newLength - MAX_ARRAY_LENGTH <= 0) { return newLength; } return hugeLength(oldLength, minGrowth); } private static int hugeLength(int oldLength, int minGrowth) { int minLength = oldLength + minGrowth; if (minLength < 0) { // overflow throw new OutOfMemoryError("Required array length too large"); } if (minLength <= MAX_ARRAY_LENGTH) { return MAX_ARRAY_LENGTH; } return Integer.MAX_VALUE; }
public boolean addAll(Collection<? extends E> c)
方法以及它的重载public boolean addAll(int index, Collection<? extends E> c)
方法//删除元素: public E remove(int index) public boolean remove(Object o) public boolean removeAll(Collection<?> c) boolean removeIf(Predicate<? super E> filter, int i, final int end) public void clear() //修改元素: public E set(int index, E element) public void replaceAll(UnaryOperator<E> operator) //查询/得到元素: public E get(int index) public int indexOf(Object o) public List<E> subList(int fromIndex, int toIndex)
根据官方文档的描述,ArrayList是一个非线程安全的容器,两个线程能够同时对一个ArrayList进行读、写操做。一般来讲对封装了ArrayList的类进行了同步操做后就能确保线程安全。多线程
Note that this implementation is not synchronized. If multiple threads access an
ArrayList
instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements,or explicitly resizes the backing array; merely setting the value of an element is not a structural modification.)ide
固然,ArrayList实现中也经过fail-fast确保了不正确的多线程操做会尽快的抛出错误,防止Bug隐藏在程序中直到将来的某一天被发现。函数
modCount
,该变量在ArrayList执行结构性的修改(structural modification)时会 +1,如add、remove、clear等改变容器size的方法,而在set方法中不自增变量(但使人迷惑的是replaceAll和sort方法却会修改modCount
的值,总结来讲不该该依赖modCount实现的fail-fast机制)//java.util.AbstractList.java protected transient int modCount = 0;
modCount
赋值给一个expectedModCount
变量,在对两个容器内的元素一一进行完比较判断后得出两个对象是否相等的判断,但在返回判断以前要问一个问题,在对比判断的过程当中当前这个ArrayList(this)有没有被其余人(线程)动过?因此加了一个checkForComodification
方法进行判断,若是modCount
与原先不一样则表明该ArrayList通过改动,则equals的判断结果并不可信,抛出throw new ConcurrentModificationException()
异常//java.util.ArrayList.java public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof List)) { return false; } final int expectedModCount = modCount; // ArrayList can be subclassed and given arbitrary behavior, but we can // still deal with the common case where o is ArrayList precisely boolean equal = (o.getClass() == ArrayList.class) ? equalsArrayList((ArrayList<?>) o) : equalsRange((List<?>) o, 0, size); checkForComodification(expectedModCount); return equal; } private void checkForComodification(final int expectedModCount) { if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }
我使用代码模拟了在使用迭代器的状况下throw new ConcurrentModificationException()
的抛出工具
public class failFastTest_02 { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { List<Integer> list = new ArrayList<>(); int changeIndex = 5; for(int i=0;i<10;i++){ list.add(i); } Iterator iterator = list.iterator(); //反射获取expectedModCount Field field = iterator.getClass().getDeclaredField("expectedModCount"); field.setAccessible(true); //反射获取modCount Class<?> l = list.getClass(); l = l.getSuperclass(); Field fieldList = l.getDeclaredField("modCount"); fieldList.setAccessible(true); while(iterator.hasNext()){ if(changeIndex==0){ list.add(-42); } System.out.println("Value of expectedModCount:" + field.get(iterator)); System.out.println("Value of modCount:" + fieldList.get(list)); System.out.println("iterator get element in list "+ iterator.next()); changeIndex--; } } }
getClass()方法来获取类的定义信息,经过定义信息再调用getFields()方法来获取类的全部公共属性,或者调用getDeclaredFields()方法来获取类的全部属性,包括公共,保护,私有,默认的方法。可是这里有一点要注意的是这个方法只能获取当前类里面显示定义的属性,不能获取到父类或者父类的父类及更高层次的属性的。使用Class.getSuperClass()获取父类后再获取父类的属性。源码分析
expectedModCount
不会由于ArrayList方法对列表的修改而改变,在这以后对于该列表(ArrayList)的结构性修改都会致使异常的抛出,这确保了迭代器不会出错(迭代器使用 cursor
维护状态,当外界的结构变化时 size
改变,不使用fail-fast public boolean hasNext() {return cursor != size;}
可能会产生错误结果),若是想在使用迭代器时修改列表,应该使用迭代器自带的方法。上述代码报错以下。 cursor
顾名思义跟光标同样,读取一个元素后要将光标向后移动一格,删除一个元素则是将光标前的一个元素删除,此时光标随之退后一格。固然,ArrayList迭代器不能一直退格(remove),必需要先能读取一个元素而后才能将其删除elementData
数组中,使用无参构造函数时,加入第一个元素后elementData
数组大小为10。new ArrayList<>().size()
为列表储存真实元素个数,不为列表容量fail-fast相关:http://www.javashuo.com/article/p-kutmancs-ne.html
https://baijiahao.baidu.com/s?id=1638201147057831295&wfr=spider&for=pc
内部类访问外部类私有变量:https://blog.csdn.net/qq_33330687/article/details/77915345