赞
踩
IDEA 进入 ArrayList 源码类,按 ctrl+alt+u 即可
蓝色实线:类 继承 类
绿色实线:接口 继承 接口
绿色虚线:类 实现 接口
本文主要是对 ArrayList 类作一个自己学习记录,对于其他涉及的类点到为止
下面正式介绍今天的主角:ArrayList
在平时的项目中 ArrayList 是用到最多的 Java 内部集合之一,当然另外的就是 HashMap。在此之前可以看看这篇文章:数组与ArrayList
在 IDEA 中快捷键:Ctrl + F12 会显示本类的方法和变量等
/** * Default initial capacity. 默认初始化数组大小(注意是在第一次真正添加数据时,才会初始化) */ private static final int DEFAULT_CAPACITY = 10; /** * 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 = {}; /* 上面为什么会定义两个空数组,待会下面会分析到 */ /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ // 底层数组:真正数据存放的位置 transient Object[] elementData; // non-private to simplify nested class access /** * The size of the ArrayList (the number of elements it contains). * * @serial */ // 保存在数组中元素的个数(取数据长度,很方便) private int size;
/** * Constructs an empty list with the specified initial capacity. * * @param initialCapacity the initial capacity of the list * @throws IllegalArgumentException if the specified initial capacity * is negative */ 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); } } /** * Constructs an empty list with an initial capacity of ten. * */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ 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; } }
//每次添加在数组最后一个元素的位置 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++; }
两者都有 ensureCapacityInternal ,不同的是,指定位置添加,需要复制元素,这也很好理解:在数组中间添加一个元素,需要把插入位置后面的元素都往后移一个位置。就是给新元素腾位置的
二: private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } 一: private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } 三: private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
从 add 函数进入后,上面三个函数的执行顺序如上
第一个函数只是承接了参数,没有实质做用
第二个函数:calculateCapacity 直译就是:计算容量,
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//只有当使用空构造函数,初始化对象才会走 if 语句
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
第三个函数 ensureExplicitCapacity :确保明确的容量:
首先就将 modCount 加一操作,这个字段的值,涉及 fail-fast;再判断容量是否够用
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
最后就是真正增加底层数组容量的函数:
主要看信数组的大小:int newCapacity = oldCapacity + (oldCapacity >> 1);
右移1位,相当于除以2,所以这就是为什么说 ArrayList 扩容是增加自身容量的 1.5 倍
Arrays.copyOf 最终也是调用 System 下面的 native 方法:arraycopy
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);
}
// 根据下标,删除一个数据(就是覆盖) 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){}
Java7: public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; } Java8: 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); } }
当容量是 0 的时候,避免产生多余的空数组
在计算容量的时候不同
Java7: private void ensureCapacityInternal(int minCapacity) { if (elementData == EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } Java8: private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
Java8 是以 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 作为比较,即在调用空构造函数的情况下才会走 if 语句 :
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
其他情况下就不会走这句代码。
EMPTY_ELEMENTDATA是为了优化创建ArrayList空实例时产生不必要的空数组,使得所有ArrayList空实例都指向同一个空数组。DEFAULTCAPACITY_EMPTY_ELEMENTDATA是为了确保无参构成函数创建的实例在添加第一个元素时,最小的容量是默认大小10。
整体来说:ArrayList 集合类的源码不难,看到变量中有两个空数组的时候,可能会有些许疑惑,但是对比这 Java7 源码看看,就清楚的多了。我们学习的时候,也要多采用这种对比学习法方法,这样理解的更快、更好!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。