ArrayList
当我还在初学java的阶段的时候,看别人的代码,会发现ArrayList出场率非常高,仿佛ArrayList和List是绑定一样的,"List<String> list = new ArrayList<>();",那时候天真的我没有细究这些集合的异同,但是在工作两年之后,每次选用一个集合的时候,我都会思考以下几个问题:
1.我这个集合是什么样的规模?
2.我对这个集合会做什么操作?
3.高并发场景下直接使用会出现什么问题?
...
介绍
ArrayList的底层实现是数组,确切说的是可动态扩容的数组
数组特性
既然是数组,那就会继承数组的优势,随机访问。
随机访问一般指随机存取。在计算机科学中,随机存取(有时亦称直接访问)代表同一时间访问一组序列中的一个随意组件。
从这个优势可以知道频繁对集合内元素访问的场景使用ArrayList。
动态扩容能力
接下来就来看数组是如何动态扩容的
当需要的数组的容量大于现有数组容量,就要对现有的elementData进行扩容,贴一下扩容的源码:
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code ->考虑溢出,jdk源码经常出现
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);// 1.5倍扩容,做计算尽量使用位移,性能考虑
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);// 可以看出这个函数是ArrayList动态扩容的核心!!!
}
接下来看扩容的核心源码,将涉及的源码都贴在下面,细心食用~
/**
* Copies the specified array, truncating or padding with nulls (if necessary)
* so the copy has the specified length. For all indices that are
* valid in both the original array and the copy, the two arrays will
* contain identical values. For any indices that are valid in the
* copy but not the original, the copy will contain <tt>null</tt>.
* Such indices will exist if and only if the specified length
* is greater than that of the original array.
* The resulting array is of exactly the same class as the original array.
*
* @param <T> the class of the objects in the array
* @param original the array to be copied
* @param newLength the length of the copy to be returned
* @return a copy of the original array, truncated or padded with nulls
* to obtain the specified length
* @throws NegativeArraySizeException if <tt>newLength</tt> is negative
* @throws NullPointerException if <tt>original</tt> is null
* @since 1.6
*/
@SuppressWarnings("unchecked")
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
/**
* Copies the specified array, truncating or padding with nulls (if necessary)
* so the copy has the specified length. For all indices that are
* valid in both the original array and the copy, the two arrays will
* contain identical values. For any indices that are valid in the
* copy but not the original, the copy will contain <tt>null</tt>.
* Such indices will exist if and only if the specified length
* is greater than that of the original array.
* The resulting array is of the class <tt>newType</tt>.
*
* @param <U> the class of the objects in the original array
* @param <T> the class of the objects in the returned array
* @param original the array to be copied
* @param newLength the length of the copy to be returned
* @param newType the class of the copy to be returned
* @return a copy of the original array, truncated or padded with nulls
* to obtain the specified length
* @throws NegativeArraySizeException if <tt>newLength</tt> is negative
* @throws NullPointerException if <tt>original</tt> is null
* @throws ArrayStoreException if an element copied from
* <tt>original</tt> is not of a runtime type that can be stored in
* an array of class <tt>newType</tt>
* @since 1.6
*/
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));// 划重点 扩容底层实现System.arraycopy()
return copy;
}
虽然扩容的效率不差,但是也经不起频繁的调用,在创建的时候就要格外注意,给ArrayList赋予一个合适的大小,避免频繁扩容!
构造函数
既然说了创建,那我们对于我们使用需要了解的就是它的构造函数
(1)无参数构造
(2)指定大小:对于使用,最好使用这种,给予一个合适的大小,减少频繁的扩容,提升我们的代码性能
(3)接收一个Collection对象
“慢”说明
其实ArrayList因为是本身底层数组,在一些删除某个元素或者在中间添加一个元素,他就需要对数组进行拷贝,这种拷贝肯定是不如链式结构更换指向快,同时如果不给定一个合适的大小频繁扩容,可以再回头看看grow函数的源码。
使用小结
读完介绍就会很容易地总结出来,使用ArrayList的几个原则
(1)当需要频繁访问集合内的元素的时候,优先使用ArrayList
(2)给一个合适的大小,避免频繁扩容,也不至于影响性能
(3)当然如果数组已经足够满足你的需求,那就使用数组吧
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。