1、ArrayList的使用
demo:
//创建一个ArrayList数组
List<String> list = new ArrayList<>();
//向ArrayList数组中添加元素
list.add("Test");
list.add("test");
System.out.println(list);
//输出["Test","test"]
//获取ArrayList中的元素
list.get(index);
//删除ArrayList中的元素
list.remove();
//....
2、ArrayList的继承体系
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//.....
}
从 继承图 和 源码 中我们能看见 ArrayList 实现了 List、RandomAccess、Cloneable、Serializable 等。
实现了 List 可以表明 ArrayList 具有增删改查和遍历等操作。
实现了 RandomAccess 表明 ArrayList 具有随机访问等能力。
实现了 Cloneable 表明 ArrayList 可以被 浅拷贝 。
实现了 Serializable 表明 ArrayList 可以被序列化。
3.扩容机制源码:
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 判断是否需要扩容
elementData[size++] = e;
return true;
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 判断当前数组是否是默认构造方法生成的空数组,如果是的话minCapacity=10反之则根据原来的值传入下一个方法去完成下一步的扩容判断
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);
}
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
// ArrayList数组中最大的值为Integer的最大值- 8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 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
// 获取当前数组的长度
int oldCapacity = elementData.length;
// 把当前的数组长度扩展为之前的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 进行判断,如果扩容后还是小于所需长度,则把扩容长度变为最小长度。
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果所需最小扩容长度大于ArrayList中最大的长度
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) {
// 如果越界,则抛出 OutOfMemoryError 异常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 判断如果最小值在 Integer.MAX_VALUE - 8 到 Integer.MAX_VALUE 范围就直接使用其中的范围值。
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
通过上述 流程图 和 源码 的结合,我们能发现一下核心点:
int newCapacity = oldCapacity + (oldCapacity >> 1); 扩容的计算方式。一看是 1.5 倍,其实是不完全正确的。因为oldCapacity >> 1必须是整数。
比如:第一次扩容:应该是 0 + 0/2 = 0 ,
然后通过 if (newCapacity - minCapacity < 0) newCapacity =
minCapacity;将默认值10 赋值给newCapacity。所以无参构造第一次扩容到10。第二次扩容:是 10 + 10/2 = 15;
第三次扩容: 15 +15/2 = 22; 注意15/2 = 7 ,不是7.5。
通过第三步扩容可以得知,扩容倍数不是1.5倍。实际上是 a = a+a/2。当a 为偶数时,才是1.5倍。
所以所说,只能是 约等于1.5倍 。
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);} 判断当前数组是否是默认构造方法生成的空数组,如果是的话minCapacity=10反之则根据原来的值传入下一个方法去完成下一步的扩容判断。
4、ArrayList常见面试题
4.1、 ArrayList的扩容机制是什么样子的?
4.2、 ArrayList是线程安全的吗?
4.3、 ArrayList 和 LinkedList 的区别?
4.4、 ArrayList 如何解决频繁扩容带来的效率问题?
4.5、 ArrayList插入或删除元素是否一定比LinkedList慢?
4.6、 如何复制某个ArrayList到另外一个ArrayList中去呢?你能列举几种?