开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 9 天,点击查看活动详情
IDEA 查看 ArrayList 类结构图
IDEA 进入 ArrayList 源码类,按 ctrl+alt+u 即可
蓝色实线:类 继承 类 绿色实线:接口 继承 接口 绿色虚线:类 实现 接口
本文主要是对 ArrayList 类作一个自己学习记录,对于其他涉及的类点到为止
- RandomAccess 我们点进去发现,这就是一个空架子,一个标记接口。表示实现了此接口,也就能进行随机访问。 参考文章
- Serializable 也是一个标记接口,表示对象可以序列化与反序列化。 参考文章
下面正式介绍今天的主角:ArrayList 在平时的项目中 ArrayList 是用到最多的 Java 内部集合之一,当然另外的就是 HashMap。在此之前可以看看这篇文章:数组与ArrayList
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;
}
}
add 函数
//每次添加在数组最后一个元素的位置
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);
}
remove
// 根据下标,删除一个数据(就是覆盖)
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){}
DEFAULTCAPACITY_EMPTY_ELEMENTDATA
- 避免不必要的空间浪费 在 Java7 中是只有 EMPTY_ELEMENTDATA 这一个空数组的,为什么 Java8 添加了一个 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 呢?且也是一个空数组。
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 源码看看,就清楚的多了。我们学习的时候,也要多采用这种对比学习法方法,这样理解的更快、更好!