集合(三)HashMap源码分析
ArrayList是什么?
颤抖的小手敲开门ArrayList的大门,发现她竟然是Object[] elementData数组。
构造函数
我们在使用集合的时候都知道集合长度是随着插入删除变化的,而在创建数组时时需要指定长度的,那ArrayList是如何做到长度可变数组呢
我们发现ArrayList有两个构造函数,一个无参(我们经常用)一个有参(传入一个int值,这个int就是初始数组的长度)
无参构造会在我们插入数据时候默认数组长度为10,当我们在创建时指定了长度,就是初始化数组的长度。
这里有一个关键字transient,这个关键字的作用就是在序列化时忽略该变量,ArrayList是集成Serializable的可以序列化反序列化操作的。
add数据
我们在使用集合时可以无限的添加数据的,这默认长度10,ArrayList是如何做到扩容的
这10只是数组的长度,我们集合的长度是根据size变量来的,集合会在插入数据是size+1然后去和数据的长度比较,如果超出了最大容量,就会创建一个长度为10+10/2(JDK8的操作)长度的数组,然后把原数组的内容复制到新数组中,这就完成了扩容。
ArrayList在添加一个数据时做了三件事:
- size+1是否在集合容量之内ensureCapacityInternal(English是不是很吊),其实就是判断是否需要扩容;
- 数据添加到索引为size+1这个位置;
- 返回true。
看源码会知道,扩容就是扩大之前的容器长度向右移1位。大家也可能注意到默认分配的数组大小为integer的最大值-8,这是因为
一些vm在数组中保留一些头字。尝试分配较大的数组可能会导致OutOfMemory错误:请求的数组大小超过了虚拟机限制(百度翻译- -)。
ArrayList又是如何在指定索引处添加数据呢
集合在指定index插入数据,首先会复制数组index开始到最后一个值的数据,然后在index+1位置开始放,腾出index的位置给新值插入,当然这里面也要先判断容量问题。(如下图在3的位置插入一个新值)
remove删除数据
我们知道在我们遍历ArrayList的时候删除数据会报错java.util.ConcurrentModificationException ,why?
移除数据的操作也是copy指定位置+1之后的数组移动到指定位置,直接看图说话。
这就也可解释为什么遍历时移除数据会报错,5覆盖了4的位置,那么5就无法被遍历到了。当然如果我们从大到小遍历就不会出现这个问题了。
ArrayList在增删时候需要去copy数据,如果超出最大数组长度还要去扩容什么的,所以说ArrayList的增删效率是低的。
ArrayList的内存连续性使其遍历速度相对较快,所以查找就快。