LinkedList源码剖析:
LinkedList是List的一个实现类,也是一个集合容器.它的底层实现是一个双链接列表实现,在查询上面比起ArrayList差了呢么一点,但是在增加和删除元素时的效率却远远高于ArrayList.我们来一探究竟
源码:
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
}
LinkedList实现了List接口,Deque接口,而Deque接口则是实现了Queue接口,因此Deque是一个线性的collention,支持在两端插入和移除元素名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。大多数 Deque 实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。
LinkedList的构造方法:
public LinkedList(Collection<? extends E> c) {
this();
addAll(c); //传入一个Collection类型的集合来初始化容器
}
public LinkedList() {
}
我们试着往集合中插入一个元素,看看使用的方法:当我们使用add( E e)时,发现调用的是linkLast方法 ,该方法中的Node是对LinkedList中所有元素操作的数据载体,我们发现在添加的过程中并没有一行代码显示抛出异常,说明在添加元素的时候是链表式的添加,并不会出现像ArrayList中的IndexOutOfBoundsException
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null); //初始化数据载体
last = newNode;
if (l == null)
first = newNode; //如果last 也就是最后一个元素是null的话,呢么添加进来的就是第一个元素
else
l.next = newNode; //否则是下一个元素
size++;
modCount++;
}
向指定位置添加元素,该集合并没有初始化长度,
向某个位置添加某个元素的时候,整体的过程如下:
1 先判断集合的长度是否和下标相等,相等的话就直接在集合尾部添加该元素
2 如果不是的话,判断下标在集合中的位置,提高查找效率,因为后边是利用循环查找这个下标的元素,返回这个下标在集合中的Node信息,
3 使用linkBefore方法做一件事情,
在添加的时候我们并不需要去像ArrayList一样去不断的复制数组到新的数组,而是直接改变链表内部node节点存储的信息即可快速的向集合中添加元素,因此,在向中间部分添加元素这一过程而言,LinkedList比ArrayList集合要快速的多
并且LinkedList中还提供了很多的便捷的添加方法像:
addFirst( E e):将元素添加到该集合的最前面: add方法内部调用的私有的linkFirst()方法
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
addLast( E e ) 调用的是linkLast(E e)方法
移除指定位置的某一个元素remove(int index)方法,
public E remove(int index) {
checkElementIndex(index);//检查下标是否合法
return unlink(node(index));
}
//调用unlike方法
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;//前一个元素如果等于null 呢么下一个元素就是第一个
} else {
prev.next = next; //将上一个元素的下一个元素改为自己的下一个元素
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
话不多说,看图,
通过对双向链表的操作我们还可以实现,单向队列,双向队列 和栈的功能
例如 单向队列:peek()方法,获取但不移除队列的第一个元素
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
getFirst() :返回队列的第一个元素
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
poll():获取并移除队列的第一个元素
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
像双向队列:offerFirst(E e) 方法 offerLast(E e)方法 peekLast()方法 peekFirst()方法
栈操作:push(E e) pop() 方法,其中的方法还有很多,可以去查一下官方的API文档
总结:
1 linkedList 的底层实现其实是一个双向链表,创建LinkedList对象实例的时候会先加载它的数据节点,初始化的linkedList并没有容量,
2 无论是增删改查还是队列 栈的实现,都是在操纵数据节点之间的链接点,添加的时候需要更新三个节点的数据信息,从数据结构过来看,在查询方面会略逊色于ArrayList,但是在添加或删除元素的时候会比ArrayList效率快很多,因为不需要去copy数组到新数组
3 LinkedList在删除元素的时候所占用的内存会自动缩小,并不需要像ArrayList一样去trimToSize()
4 LinkedList的所有方法也都没有进行同步,因此它也不是线程安全的,因此,我们在考虑线程安全时,需要自己手动的去加一些线程同步机制
5 可以使用LinkedList模拟一些简单的数据结构
使用LinkedList模拟一个队列(FIFO容器)的简单数据结构
将myGet中的.removeLast更改为removeFirst()就模拟了一个栈(先进后出)
package Collection.List.LinkedList;
import java.util.LinkedList;
class MoniDuiLie{
private LinkedList link;
public MoniDuiLie() {
link = new LinkedList();
}
public void myAdd(Object o){
link.addFirst(o);
}
public Object myGet(){
return link.removeLast();
}
public boolean isNull() {
return link.isEmpty();
}
}
public class Duilie {
public static void main(String[] args) {
MoniDuiLie dl = new MoniDuiLie();
dl.myAdd("java01");
dl.myAdd("java02");
dl.myAdd("java03");
dl.myAdd("java04");
while(!dl.isNull())
{
System.out.println(dl.myGet());
}
}
}