1 死记硬背
1 ArrayList底层是数组elementData,用于存放插入的数据,初始大小是0,当有数据插入时,默认大小DEFAULT_CAPACITY = 10。
2 当ArrayList中的元素数量达到当前容量时,ArrayList会自动增加其容量,大小一般为原来数组的1.5倍。
2 简介
ArrayList是Java集合框架中的一个重要的类,它继承于AbstractList,实现了List接口,是一个长度可变的集合,提供了增删改查的功能。集合中允许null的存在。ArrayList类还是实现了RandomAccess接口,可以对元素进行快速访问。实现了Serializable接口,说明ArrayList可以被序列化,还有Cloneable接口,可以被复制。和Vector不同的是,ArrayList不是线程安全的。
3 三个构造器
// 1 无参构造器
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 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);
}
}
// 3 包含初始化元素的构造器
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;
}
}
4 扩容机制
1 先上代码
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add("面试题解析");
System.out.println(list.size());
}
2 代码详解
ArrayList list = new ArrayList<>();
这个代码是创建了一个ArrayList的对象list,而扩容机制发生在list.add的方法里,我们看下add方法的源码。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
此时的E e你可以理解成我们代码里传的字符串“面试题解析”,而此时的size是0,所以我们要去看下ensureCapacityInternal这个方法的源码。
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
此时的minCapacity可以理解成是1,因为是上面的size+1传过来的,而size是0,所以minCapacity就是1了,因为初始化的时候elementData设置成了
DEFAULTCAPACITY_EMPTY_ELEMENTDATA,所以就会走minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);这行代码,DEFAULT_CAPACITY是10,所以minCapacity就变成了10,接下来走方法ensureExplicitCapacity,我们也看下源码。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
此时的minCapaticy为10,而modCount是指当前集合被修改的次数,主要包括添加和删除,此时modeCount的值为0,当执行了一次add操作后,就变成了1,minCapacity是指要求的最小的容量,而elementData是指已经有的元素数组,当要求的最小的容量大于已经有的数组大小时,就会执行扩容操作,也就是grow方法。
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);
}
在grow方法中,首先我们会计算老的数组的容量为oldCapacity,本例中为0,而扩容后的容量为oldCapacity的1.5倍,为啥是1.5倍呢?看下代码:int newCapacity = oldCapacity + (oldCapacity >> 1);新容量newCapacity是老容量加上老容量右移一位的容量,而右移一位就相当于原来的容量除以2,即0.5倍,再加上前面的oldCapacity,就是1.5倍的oldCapacity,也是文章刚开始种死记硬背中说的1.5倍。一般这种情况就满足扩容需求了,但是仍然有两种情况比较特殊。
第一种就是:newCapacity - minCapacity < 0,即扩容后的容量仍然不能满足最低容量的需求,则newCapacity的大小就以minCapacity为准,这种情况是大于1.5倍的扩容的。
第二种就是:newCapacity - MAX_ARRAY_SIZE > 0,即新扩容后的大小大于2147483639,则会走新的方法hugeCapacity。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
当minCapacity小于0时,则会报OOM异常,当大于MAX_ARRAY_SIZE,最后的容量大小就等于2147483647,否则的话就等于2147483639。
附上扩容机制的时序图,希望大家能更清楚地掌握扩容机制。
总结
ArrayList的扩容机制主要是由于在添加元素时,数组的容量不够导致扩容,所以我们在创建ArrayList的时候,尽可能地指定大小,然后再进行添加操作,这样就可以避免后期的操作频繁地发生扩容而导致性能下降,从而可以提升代码的性能。如果本文讲的不够详细,有不清楚的地方,欢迎留言,都会一一回复,感谢支持。
【温馨提示】
点赞+收藏文章,关注我并私信回复【面试题解析】,即可100%免费领取楼主的所有面试题资料!
本文暂时没有评论,来添加一个吧(●'◡'●)