ArrayList源码学习
Posted nyfor2018
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ArrayList源码学习相关的知识,希望对你有一定的参考价值。
ArrayList是一个继承了AbstractList类的(注意modCount变量,是从这里继承到的),实现了List、RandomAccess、Cloneable、Serializable接口的,基于数组的集合类。
读源码从来都不是一件很容易的事情,但是还是要开始呀。
所以一起来学习吧!
在读源码的过程中其实有这么两个个小问题,在源码后面会po出来,看看你有没有这些疑问?
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { //序列化编号 private static final long serialVersionUID = 8683452581122892189L; //默认容量 private static final int DEFAULT_CAPACITY = 10; //初始化一个空数组 private static final Object[] EMPTY_ELEMENTDATA = {}; //默认空数组 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //ArrayList的存储容器,不参与序列化 transient Object[] elementData; // non-private to simplify nested class access //ArrayList中的元素容量 private int size; //初始化带参的ArrayList 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); } } //初始化无参的ArrayList public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } //初始化带集合的ArrayList,如果不是Object类型的元素,那么就直接使用Arrays工具来复制数组 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的大小改成现在所拥有的元素个数 public void trimToSize() { modCount++; //modCount的作用是用来记录ArrayList的修改次数 if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } } //如果数组是默认的空数组,则minExpand为0,否则则为10,如果最小容量大于最小扩展量,则将数组扩容为最小容量 public void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // any size if not default element table ? 0 // larger than default for default empty table. It‘s already // supposed to be at default size. : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } //如果数组是空数组,则在默认容量和参数minCapacity选最大值,作为minCapacity的值 private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } //集合进行了修改,modCount加1,如果原数组大小比参数minCapacity大,则进行扩容处理 private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //扩容操作 //先用oldCapacity记录原数组大小 //用newCapacity记录原数组扩容1.5倍的大小,其中>>表示二进制的向右移1位,效果相当于除于2 //如果newCapactiy比参数minCapacity小就使用minCapacity作为扩容之后数组容量的值 //如果minCapacity比设定好的数组的最大值还要大,就使用Integer.MAX_VALUE来作为新数组的容量 private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } //返回数组存储元素的数目 public int size() { return size; } //检测数组是否有元素 public boolean isEmpty() { return size == 0; } //检测数组中是否包含特定元素o //如果返回的是一个大于-1的整数,说明存在这样的元素 public boolean contains(Object o) { return indexOf(o) >= 0; } //如果元素为null,那么就查找数组中是否有值为null的元素 //如果元素不为null,则查找是否有特定元素 //有则返回下标位置,无则返回-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; } //功用与indexOf(Object o)相同,但查找方向相反 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; } //创建一个新的数组,并把之前数组的所有元素复制一遍,但是区别在于,modCount为0,即产生了一个新的数组 public Object clone() { try { ArrayList<?> v = (ArrayList<?>) super.clone(); v.elementData = Arrays.copyOf(elementData, size); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn‘t happen, since we are Cloneable throw new InternalError(e); } } //将ArrayList中的元素以数组的形式返回 public Object[] toArray() { return Arrays.copyOf(elementData, size); } //返回指定类型的数组,注意这里使用的不是Arrays.copyOf(),而是System.arraycopy() @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { if (a.length < size) // Make a new array of a‘s runtime type, but my contents: return (T[]) Arrays.copyOf(elementData, size, a.getClass()); System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } //返回指定位置的元素 @SuppressWarnings("unchecked") E elementData(int index) { return (E) elementData[index]; } //先对指定位置进行范围检测,然后再进行指定位置元素返回 public E get(int index) { rangeCheck(index); return elementData(index); } //对指定位置的元素的值重设为element,返回重设前的数值 public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; } //此操作有对modCount进行自增 //添加元素的时候有对原数组大小进行判断,如果数组仍有位置,则插入新元素,如果没有就扩容 public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } //先对指定位置进行检测,然后对数组容量进行检测 //将指定位置及其之后的元素都向后退一个位置 //再把新元素放到指定位置上 public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } //移除指定位置的元素 //给数组多出一个空位子出来,用于进行GC处理 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; } //移除第一个出现的指定元素 //先找到指定元素在数组中的位置,然后再进行移除操作 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 } //把集合中的元素全部清空 public void clear() { modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0; } //将指定集合中的元素都添加进集合中 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; } //把指定范围的元素都移除 protected void removeRange(int fromIndex, int toIndex) { modCount++; int numMoved = size - toIndex; System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved); // clear to let GC do its work int newSize = size - (toIndex-fromIndex); for (int i = newSize; i < size; i++) { elementData[i] = null; } size = newSize; } //检测指定位置是否在存有元素的数组范围内 //用于元素查找功能 private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } //检测指定位置是否合法 //用于元素添加功能 private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } //如果指定位置不合法,则返回提示信息 private String outOfBoundsMsg(int index) { return "Index: "+index+", Size: "+size; } //先判断集合是否合法 //然后将不存在于指定集合中的元素全部移除 public boolean removeAll(Collection<?> c) { Objects.requireNonNull(c); return batchRemove(c, false); } //把存在于指定集合中的元素从集合中全部移除 public boolean retainAll(Collection<?> c) { Objects.requireNonNull(c); return batchRemove(c, true); } //这是一个有着类似开关装置的方法 //如果complement为false,则移除所有指定集合中没有的元素 //如果complement为true,则移除所有指定集合中有的元素 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; } //流操作方法,将数组对象写入流中 private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out size as capacity for behavioural compatibility with clone() s.writeInt(size); // Write out all elements in the proper order. for (int i=0; i<size; i++) { s.writeObject(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } //流操作方法,将信息从流中读出 private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { elementData = EMPTY_ELEMENTDATA; // Read in size, and any hidden stuff s.defaultReadObject(); // Read in capacity s.readInt(); // ignored if (size > 0) { // be like clone(), allocate array based upon size not capacity ensureCapacityInternal(size); Object[] a = elementData; // Read in all elements in the proper order. for (int i=0; i<size; i++) { a[i] = s.readObject(); } } } //迭代器,返回内部类实例 public ListIterator<E> listIterator(int index) { if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: "+index); return new ListItr(index); } //迭代器,返回内部类实例 public ListIterator<E> listIterator() { return new ListItr(0); } //迭代器,返回内部类实例 public Iterator<E> iterator() { return new Itr(); } }
问题1:EMPTY_ELEMENTDATA与DEFAULTCAPACITY_EMPTY_ELEMENTDATA区别?
/** * Shared empty array instance used for empty instances. */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
我们可以看到,第一个数组的解释是: Shared empty array instance used for empty instances.翻译一下就是:用于空实例的共享空数组实例;第二个数组的解释是:
Shared empty array instance used for default sized empty instances.翻译是用于默认大小的空数组的共享空数组实例。所以我们可以知道,这两个常量的自我定义是不一样的。
接下来这一句:We distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when first element is added.即
我们将此与EMPTY_ELEMENTDATA区分开来,以便了解在添加第一个元素时要膨胀多少。
其实可以理解为,这两个常量是用来在添加第一个元素的时候,判断采用什么样的扩容方式。详情可以比对add之后的一系列方法,包括ensure系列的方法。
具体可以看着这篇文章,写的不错:https://www.jianshu.com/p/ab7c04d64899
问题2:为什么数组的最大容量是Integer.MAX_VALUE-8?
比较能说服我的解释是,数组对象的结构和形状本质上跟类对象相似,那么如果要表述一个完整的数组对象,还需要在数组中标识这个数组的大小,而这个元数据(数组长度)需要用八个字节来存储。
那么这个数组对象里面包括了什么呢?
① Class:指向描述对象类型的指针。
② 标志:描述状态对象的标志集合。
③ 锁定:标识对象是否当前同步。
④ 大小:数组的大小。
大家可以去看这篇文章https://blog.csdn.net/ChineseYoung/article/details/80787071
先写到这里啦,因为只是刚开始学,还没有怎么扩宽自己的眼界,希望有什么缺漏的大家可以提醒一下呀,有什么建议也可以提一下,共同进步~。
参考:
https://www.jianshu.com/p/ab7c04d64899
https://blog.csdn.net/ChineseYoung/article/details/80787071
https://blog.csdn.net/java_lifeng/article/details/80938123
以上是关于ArrayList源码学习的主要内容,如果未能解决你的问题,请参考以下文章