共计 3325 个字符,预计需要花费 9 分钟才能阅读完成。
注:本系列文章中用到的 jdk 版本均为
java8
ArrayList
类图如下:
ArrayList
的底层是由数组实现的,数组的特点是 固定
大小,而 ArrayList
实现了 动静扩容
。
ArrayList
局部变量如下,在上面的剖析中会用到这些变量。
/**
* 默认容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空的对象数组
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 无参结构器创立的空数组
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 存放数据的数组的缓存变量
*/
transient Object[] elementData;
/**
* 元素数量
*/
private int size;
一、初始化 ArrayList
初始化 ArrayList
个别会应用以下两个结构器
1.1 无参结构器
初始化 ArrayList
的时候如果不指定大小,则会创立一个空数组。
public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}
1.2 指定数组大小的结构器
创立一个 预估 大小的数组,指定大小后只是指定了数组初始值的大小,不影响前面扩容,指定的益处就是能够节俭内存及工夫上的开销。
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.add(E e)源码:
public boolean add(E e) {ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
add()
中 elementData[size++] = e
很好了解,就是将元素插入第 size
个地位,而后将 size++
,咱们重点来看看ensureCapacityInternal(size + 1)
办法;
private void ensureCapacityInternal(int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
ensureCapacityInternal()
办法中判断缓存变量 elementData
是否为空,也就是判断是否是第一次增加元素,如果是第一次增加元素,则设置初始化大小为默认容量 10
,否则为传入的参数。这个办法的目标就是 获取初始化数组容量 。获取到初始化容量后调用ensureExplicitCapacity(minCapacity)
办法;
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
ensureExplicitCapacity(minCapacity)
办法用来判断是否须要扩容,如果第一次增加元素,minCapacity
为 10
,elementData
容量为 0
,那么就须要去扩容。调用grow(minCapacity)
办法。
// 数组的最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 扩容大小为原来数组长度的 1.5 倍
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);
}
grow(minCapacity)
办法对数组进行扩容,扩容大小为原数组的 1.5
倍,如果计算出的扩容容量比须要的容量小,则扩容大小为须要的容量,如果扩容容量比数组最大容量大,则调用 hugeCapacity(minCapacity)
办法,将数组扩容为整数的最大长度,而后将 elemetData
数组指向新扩容的内存空间并将元素复制到新空间。
当须要的汇合容量特地大时,扩容 1.5
倍就会十分耗费空间,因而倡议初始化时预估一个容量大小。
三、删除元素
ArrayList
提供两种删除元素的办法,能够通过 索引
和元素
进行删除。两种删除大同小异,删除元素后,将前面的元素一次向前挪动。
ArrayList.remove(int index)源码:
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;
}
删除元素时,首先会判断索引是否大于 ArrayList
的大小,如果索引范畴正确,则将索引地位的下一个元素赋值到索引地位,将 ArrayList
的大小 -1
,最初返回移除的元素。操作图如下,如果我要移除索引为1
的元素:
四、总结
ArrayList
底层是数组实现的,能够进行动静扩容,扩容大小为原来的 1.5 倍,尽管能够通过动静扩容,然而数组十分大时会特地节约空间,因而倡议初始化时预估数组大小。ArrayList
容许插入反复值和空值。ArrayList
实现了 RandomAccess
接口,反对疾速随机拜访,就是能够通过索引疾速查到某个元素,因而遍历时应用 for 循环的形式效率更高。ArrayList
是线程不平安的,能够通过 Collections.synchronizedList
将其转变为线程平安的汇合,不过个别不会应用,Vector
和 CopyOnWriteArrayList
是线程平安的,Vector
性能个别,逐步被 CopyOnWriteArrayList
取代了。
点关注、不迷路
如果感觉文章不错,欢送 关注 、 点赞 、 珍藏,你们的反对是我创作的能源,感激大家。
如果文章写的有问题,请不要吝惜文笔,欢送留言指出,我会及时核查批改。
如果你还想看到更多别的货色,能够微信搜寻「Java 旅途」进行关注。「Java 旅途」目前曾经整顿各种中间件的应用教程及各类 Java 相干的面试题。