public static void main(String[] args) {
List list = new ArrayList();
System.out.println("断点1");
list.add(1);
System.out.println("断点2");
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
list.add(7);
list.add(8);
list.add(9);
list.add(10);
System.out.println("断点3");
list.add(11);
System.out.println("断点4");
}
注意看图,elementData在断点1时标识是29,在断点2和断点3处的标识都是31,而在断点4时标识46,第2篇讲过,这说明elementData引用变量前后一共指向了三个不同的数组对象。也就是说,elementData并没有真正的扩容,而是创建了一个容量更大的数组对象来替代之前的数组,并且复制之前的数组内容。
元素类型Object
list.add(1);
list.add(1l);
假如声明时使用List,就指定了固定元素类型。而我的代码中并没有使用泛型,所以它的类型可以是任意Object,但不能是基本类型。当添加int元素时,会自动转换为Integer。当添加long元素时,会自动转换为Long。因此,最终list所有的元素类型都是引用类型(4字节),长度相同,这是实现数组高性能查询所必需的。以后讲其他集合的元素类型时,也和ArrayList是一样的原理,不再解释。
在尾部添加
第3篇在数组中添加了5亿个元素,很快就执行完成。假如用同样的方法在ArrayList中添加5亿元素会怎么样?
int size=500000000;
List list = new ArrayList();
long t1 = System.currentTimeMillis();
for(int i=0;i<size;i++){
list.add(i);
}
long t2 = System.currentTimeMillis();
System.out.println(t2-t1);
List list = new ArrayList(size);
list.add(1);
耗时:1080毫秒
add()方法默认是在尾部添加数据,ArrayList的size可以帮助数组瞬间完成定位,然后直接添加,所以这样的性能是很高的。
在指定位置添加
list.add(int index,E element)方法是在位置index处添加,如下
int size=500000000;
List list = new ArrayList(size);
long t1 = System.currentTimeMillis();
for(int i=0;i<size;i++){
list.add(0,1);
}
long t2 = System.currentTimeMillis();
System.out.println(t2-t1);
删除的性能
底层就是数组: 操作尾部数据时,其性能是最高的。 操作越靠前的数据,性能越低。
封装了数组: 操作更简便,代码可读性更高。 但是也封装了额外操作,比如安全检查,数组是否越界等,这也带来一些性能开销,所以ArrayList性能会比数组稍稍低那么一点点。
应用场景
关于从指定位置添加和删除,是ArrayList的性能缺陷。 我们要做的是将其优点发挥到其擅长的场景,将其不擅长的场景交给其他数据结构来处理,扬长避短。 后续要介绍的集合都是一样,没有哪一种结构是完美的,只有其最适合哪种场景。
最近热门:
——长按关注——