List接口底层实现类
一、java集合类分类
java集合框架分为俩种,单列集合框架Collection,和双列集合框架Map
1.单列集合框架结构
Collection接口:单列集合,用来存储一个一个的对象
-
|----List接口:存储序的、可重复的数据。 -->“动态”数组 * |----ArrayList、LinkedList、Vector
-
|----Set接口:存储无序的、不可重复的数据 -->高中讲的“集合” * |----HashSet、LinkedHashSet、TreeSet
本文将对List接口进行解析
对应图示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iT4sKTMA-1593178822089)(List接口底层实现类/1592968971414.png)]
实线为直接继承或实现,虚线为间接继承或实现
二、ArrayList
1.jdk1.7的情况
ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData
-
list.add(123);//elementData[0] = new Integer(123);
-
list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。
-
默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
-
结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)
2.jdk1.8的情况
由于现主流开发都是1.8版本的jdk,所有以下对ArrayList进行深入解读
3.继承情况
使用idea自带的可视化工具查看继承与实现情况,可以清楚的看到ArrayList间接的实现了Collection接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hG9mPXbo-1593178822091)(List接口底层实现类/1592969473(1)].jpg)
4.创建ArrayList
接下来我们来看一下创建一个ArrayList容器会发生什么
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r5JCuIh4-1593178822095)(List接口底层实现类/1592969867(1)].jpg)
当我用debug创建一个arrayList时 返回的是一个空的,长度为0的数组
我们跟进去构造函数
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
transient Object[] elementData; //一个是Object[]对象,transient关键词是指该对象不需要被序列化
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//一个空的Object[]对象
与jdk7版本的区别是 jdk7创建后就创建一个长度为10的数组,但jdk8并没有,更加节省内存
5.添加数据
arrayList.add(123);
我们用debug跟进源码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8n6JCAgu-1593178822099)(List接口底层实现类/1592970609(1)].jpg)
因为存入123是基本数据类型,这里有一个自动装箱的过程(装换为Integer类型)
后面就是进入add方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0LathqMx-1593178822101)(List接口底层实现类/1592973839(1)].jpg)
size默认初始化为0,进入ensureCapacityInternal方法
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
//max方法取俩个参数最大的一个(这里DEFAULT_CAPACITY为10),将最大的值赋给minCapacity
}
ensureExplicitCapacity(minCapacity);
}
进入ensureExplicitCapacity(minCapacity)方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//默认为0
// overflow-conscious code
if (minCapacity - elementData.length > 0)//判断最小容量减去原数组长度是否大于0
grow(minCapacity);
}
进入grow(minCapacity)方法
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;//记录老容量长度 此时elementData长度为0
int newCapacity = oldCapacity + (oldCapacity >> 1);//定义新容量值,老容量长度右移一位后加再上老容量长度,而右移一位是取数值的一半,这里就决定了ArrayList以后进行扩容都是对原数组扩容1.5倍
if (newCapacity - minCapacity < 0)//0 - 10 小于0 所有进入
newCapacity = minCapacity; //新容量长度就为10
if (newCapacity - MAX_ARRAY_SIZE > 0) //判断此容量会不会超过最大长度(Integer.MAX_VALUE - 8)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);//调用Arrays工具类,对数组进行扩容,原理为将旧数组数据复制到新数组
}
三、LinkedList
1.继承情况
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e5CKjWHC-1593178822103)(List接口底层实现类/1592975059(1)].jpg)
可以看到不仅间接的实现了List接口,还间接的实现了队列Queue的接口
2.创建LinkedList
LinkedList linkedList = new LinkedList();
通过new的方式只是初始化LinkedList容器,底层双向链表结构还未创建。LinkedList Node类型的first和last属性,默认为null
3.添加数据
linkedList.add(123);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8ZH307eo-1593178822105)(List接口底层实现类/1592975703(1)].jpg)
进入linkLast中
//在linkedList里面有first和last属性
transient Node<E> first;
transient Node<E> last;
void linkLast(E e) {
final Node<E> l = last; //将原料的last对象赋值给l
final Node<E> newNode = new Node<>(l, e, null);//Node为一个静态内部类 在下面:作用是初始化Node对象(链表对象,prev为头指针,next为尾指针,element为数据)
last = newNode;//将有数据的newNode赋值给last
if (l == null)//判断是否是第一个节点,如果是
first = newNode; //将NewNode直接赋值给尾节点
else
l.next = newNode; //不是的话。将节点添加到l.next 相当于将链表连接起来
size++;
modCount++;
}
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
四、Vector
jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
在扩容方面,默认扩容为原来的数组长度的2倍。
1.继承情况
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SBoqQmQv-1593178822107)(List接口底层实现类/1593002715(1)].png)
2.创建Vector
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WcHGuinx-1593178822109)(List接口底层实现类/1593002840(1)].jpg)
让我们跟进去构造函数
public Vector() {
this(10);
}
再往下跟,调用一个参数的构造函数
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
再往下跟,调用凉参数的构造函数
public Vector(int initialCapacity, int capacityIncrement) {
super(); //显示调用父构造函数
if (initialCapacity < 0) //为0就报错,参数传进来为10
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity]; //创建并初始化了对象数组
this.capacityIncrement = capacityIncrement;
}
上面俩个属性定义如下:
protected Object[] elementData;
protected int elementCount;
此版本为jdk8,jdk7的Vector也是创建一个Vector容器就默认初始化长度为0,与jdk7的ArrayList一样,但是人家开发者对ArrayList进行了改进,使用懒加载,可能Vector要被放弃了吧!!
接下来讲为什么会放弃Vector
3.添加数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6SEkhBSs-1593178822111)(List接口底层实现类/1593003620(1)].jpg)
接下来跟进源码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zfewpFDt-1593178822113)(List接口底层实现类/1593003749(1)].jpg)
elementCount默认为0。
可以看到有一个ensureCapacityHelper方法,此方法是校验长度是否足够,并去扩容的方法,如下:
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);//扩容方法
}
但此处因为默认初始化长度为10,所以这里不会有扩容的需求。
稍等!Vector不是挺好的嘛,为什么要放弃他??因为…
public synchronized boolean add(E e)
public synchronized E remove(int index)
public synchronized boolean containsAll(Collection<?> c)
public synchronized boolean addAll(int index, Collection<? extends E> c)
因为他的方法上全加了synchronized,加上这个字段他就是一个线程安全的方法,而他的全部方法都是线程安全方法,又众所周知,线程安全效率大大减低,所以连开发jdk人员都懒得救他,毕竟,出现了更好的人,老情人全忘掉了~
五、总结
上述介绍了List接口的全部实现类,如果数据不需要线程安全,则不要去考虑使用Vector,而因为ArrayList和LinkedList底层实现原理不同,他们使用的地方也有所不同,ArrayList底层使用的是数组实现,因此在需要频繁的插入和删除数据时效率会大大减低,而数组有一个好处,就是有下标,下标最直接好的好处就是查询速度非常的快,而LinkedList底层使用的是双向链表实现,所以在频繁的插入和删除数据时,有超高性能的表现,但是在查询的速度远远比不上ArrayList。
记重点:ArrayList数组实现,每次扩容1.5倍,使用懒加载,首次扩容长度为10
上述介绍了List接口的全部实现类,如果数据不需要线程安全,则不要去考虑使用Vector,而因为ArrayList和LinkedList底层实现原理不同,他们使用的地方也有所不同,ArrayList底层使用的是数组实现,因此在需要频繁的插入和删除数据时效率会大大减低,而数组有一个好处,就是有下标,下标最直接好的好处就是查询速度非常的快,而LinkedList底层使用的是双向链表实现,所以在频繁的插入和删除数据时,有超高性能的表现,但是在查询的速度远远比不上ArrayList。
记重点:ArrayList数组实现,每次扩容1.5倍,使用懒加载,首次扩容长度为10
LinkedList双向链表实现