ArrayList 分析
本人是基于Java8分析的。其实我认为ArrayList分为两步分析,第一步是分析它的构造方法,它的构造方法其实挺有意思的,一会慢慢讲,第二步就是分析它的Add方法,当然了,还有其他方法也会看看的,但是主要的还是Add方法
ArrayList 底层使用的是动态数组,是连续的内存空间,对于数据的查询比较快。对于插入和删除需要移动内存,所以比较慢。它是线程不安全的。
属性
// 默认容量
private static final int DEFAULT_CAPACITY = 10;
// 空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// 默认空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 存放元素的数组,从这可以发现ArrayList的底层实现就是一个Object数组
transient Object[] elementData; // non-private to simplify nested class access
// 获取size,代表的是list中真实存在的元素个数
private int size;
强调一下,这里的size代表的是list中真正存在的元素的个数,接下来分析源码
一、构造方法
ArrayList 一共有三个构造方法
- 第一个构造方法(无参构造)
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
上面的这个初始化方法就是我们使用如下方法构造出来的
List<Integer> integers = new ArrayList<>();
- 第二个构造方法(有参构造)
public ArrayList(int initialCapacity) {
// 判断参数是否大于0,如果大于0,将数组的大小设置为指定大小;
// 如果等于0,设置一个空数组;
// 如果小于0,报错。
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
看样子使用这种方法构造ArrayList比第一种复杂啊,其实他这里也没干啥。。内容比较简单,就不分析了。
使用方式:
List<Integer> integers = new ArrayList<>(10);
- 第三个构造方法(有参构造)
public ArrayList(Collection<? extends E> c) {
// 将List转变为数组;
// 判断size是否大于0,如果不等于0,直接copy;
// 否则创建一个空数组
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;
}
}
使用方式:
List<Integer> integers1 = null;
List<Integer> integers = new ArrayList<>(integers1);
不知道大家在上面的源码中,有没有看到这样的一句话,
c.toArray might (incorrectly) not return Object[] (see 6260652)
就是这句话,后来查阅之后,发现是这个官方的一个bug,具体我们就不讨论了。
以上就是ArrayList的三个构造方法。
接下来我们继续分析它的add方法
下面这就是ArrayList的add方法,是不是很简单,哈哈。
// 首先他调用了另一个方法
public boolean add(E e) {
// 强调一下,这里的size代表的是list中真正存在的元素的个数
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
下面这个方法就是add方法中的第一行代码,分析一下:
首先判断用户的数组是不是空的,如果是空的,进行比较,选择大的(注意,我这里说的空是指Object[0])
如果不满足条件,进入下面的方法。
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
方法中的第一行是一个变量,该字段表示list结构上被修改的次数;
接下来,如果添加元素后的大小大于添加元素前的大小,这进行扩容。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
重点来了,嘻嘻
下面这个方法就是ArrayList进行扩容的核心方法
首先,获取list的元素个数,进行1.5倍扩容,
接下来如果扩容后的大小小于添加元素后的大小,将添加元素后的大小赋值给扩容后的大小,
如果,扩容后的大小大于java 定义数组的最大数,进入hugeCapacity方法,
确定大小后,进行copy,将旧的元素赋值给新的数组,并设置数组容量。
此时,注意:这只是进行了扩容并将旧数据添加到了新的数组里面,别忘了add方法中还有一行代码。那一行才是想list中添加数据,到此分析结束
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;
}
其他的方法
如果你仔细看过ArrayList源码会发现,他有两个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) {
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 remove method that skips bounds checking and does not
* return the value removed.
*/
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
}
大家都看到这两个方法了吧,其实仔细一看没什么区别,最后还是看官方文档发现,为什么他叫fastRemove,上面的注释的意思大致是:私有删除方法,跳过边界检查并且不返回删除的值。由此看来,是跳过了边界检查。