上一节我们浏览了ArrayList容器,来总结一下?
- 保存数据的为一个名叫做elementData的数组默认容量为10
- 扩容大小为原容量的一半即originSize+originSize>>2的大小
- 扩容方式为创建新的数组并且通过数组的复制来完成扩容
- 删除需要挪动后面的元素填充到被删除元素的位置
那么以上就是上一节的内容回顾,那么今天我们来看看另外一个容器LinkedList:
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
可以看到,这个容器还是list接口的实现类,证明它和arraylist实现类有着相同的行为,毕竟接口是行为的抽象嘛,而且他两个都来自于list接口所以这么说没毛病,闭上眼睛想想?
那么问题来了,有了这么爽的arraylist容器,你看,它可以动态扩容,还能在O(1)时间复杂度内获取到想要的元素,即在一次访问就能获取到我们要的元素:arraylist.get(int index),这么爽为何还要弄一个linkedList出来呢?大家要知道东西呢必然有利必有弊,来看看我们的Arraylist的弊端:
- 动态扩容需要进行复制会浪费大量时间并且导致内存增大【复制过程中两个数组都同时存在对吧】
- 删除时候需要挪动大量元素【上一节课已经解释了为何要移动元素】
那么我们来看看LinkedList如何来解决这些问题,老样子,祭出源码:
/**
* Appends the specified element to the end of this list.
*贴心翻译:增加指定的数据到列表的末尾
* <p>This method is equivalent to {@link #addLast}.
*贴心翻译:这个方法等价于addLast方法
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
linkLast(e);
return true;
}
好简洁的源码,- -~就两行,那么它源码注释说着方法等价于addLast,先别管咱们先继续往下看,然后再回过头来看这个addlast是何方神圣。Ok祭出linkLast(e):
void linkLast(E e) {
final Node<E> l = last; //这里先把last这个成员变量临时赋值给l局部变量保存起来,那么既然这里提到了last成员变量,那么我们请他出来溜溜,看看是啥样:
/**
* Pointer to last node. 指向最后一个节点
*/
transient Node<E> last;
一看,原来是一个transient的Node节点呀,那么transient是个什么鬼?你不需要知道这里,因为没有啥影响在我们讲到序列化的时候再给大家解释这个关键字的含义,那么我们现在主要是看看Node类是干啥的:
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;
}
}
一看,原来如此,我这个node类是一个静态的私有内部类,用来承载数据和前一个数据和后一个数据:next指向下一个数据,prev指向上一个数据。从这里其实其实就已经看出来了它和Arraylist的区别了~
final Node<E> newNode = new Node<>(l, e, null);//构建一个新节点,把新节点的prev属性赋值给last,然后值为e,next节点的值为null
last = newNode;//最后把新节点变为last节点
if (l == null)//如果l是空,即last引用是空的,则first引用指向新创建的节点,否则last的next节点变为新创建的节点
first = newNode;
//这里出现了first那么祭出来:
/**
* Pointer to first node.指向第一个节点
*/
transient Node<E> first;
看来和last一样只不过变量名不一样罢了,好像功能还不一样对吧,一个指向最后一个,一个指向第一个~相同点就是初始都为null
else
l.next = newNode;
size++;//最后增加大小即可
modCount++;//修改次数加1
}
ok,用张图来表示这个过程:
这就是linkedlist的add过程可以发现,这玩意儿没有elementdata即数组来保存数据了,而采用next和prev这种引用的方式来保存数据,而每个节点之间彼此关联,是不是就是一条链呢,这就是链表的由来~,最后闭上眼睛想想这样子的结构是不是解决了数组扩容复制和删除挪动的弊端呢?这节课解决了数组扩容复制的弊端,那么下节课来看看它的remove操作和get操作~
温馨提醒:记得挂上自己的知识树,不然到最后又要说哎呀,我又忘了- -