jdk源码阅读二:LinkedList

来源:互联网 发布:怎么开淘宝店铺 编辑:程序博客网 时间:2024/06/05 17:30

LinkedList介绍

这里写图片描述

LinkedList是基于链表来实现的List。主要特征如下:

  • 不仅实现了List接口,还实现了Deque,所以是一个双端队列。
  • 允许存储null对象。
  • 非线程安全,如果多线程操作它,需要外部枷锁,或者Collections.synchronizedList。
  • 返回的iterator是fail-fast,如果iterator创建后,修改了list结构则会抛出异常ConcurrentModificationException, 注意这里异常表明是一个bug,需要避免,而不能处理业务逻辑。

size 变量

size 表示当前list中存储了元素的个数,初始化为0。add一个元素加一,删除一个元素减一。
它和ArrayList定义不一样,加了transient 关键字,表明序列化时候忽略该字段。

 transient int size = 0;

容量增长策略

因为是基于链表存储元素,所以容量受限于堆内存。没有扩容一说。所以不存在ensureCapacity*的方法。
接下来我们主要看看LinkedList的List和Deque功能实现。

底层存储策略

关于存储的成员变量主要如下:

transient Node<E> first;transient Node<E> last;

存储着链表的第一个和最后一个元素。

它有两个构造函数:

public LinkedList() {}public LinkedList(Collection<? extends E> c) {    this();    addAll(c);}

调用默认的构造函数:first,last都是null,size =0。
调用第二个构造函数,添加了一个集合到list。

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类代码很简单:

  • item存储具体对象。
  • next表示该节点对应的下一个节点。
  • prev表示该节点对应的上一个节点。

常用curd操作

插入元素

有六个add*相关方法,

add(E e)

内部调用了linkLast(e)。

void linkLast(E e) {    final Node<E> l = last;    final Node<E> newNode = new Node<>(l, e, null);    last = newNode;    //判断list是否第一次插入元素     if (l == null)        first = newNode;    else        l.next = newNode;    size++;    modCount++;}

每次末尾插入一个新元素,并别该元素的prev元素也添加进去。这里要说明第一次插入元素之前 list的fist和last都为null。

add(E e)

public void add(int index, E element) {    //检查index是否合法,index在[0,size]之内,否则抛出异常    checkPositionIndex(index);    //在末尾插入元素    if (index == size)        linkLast(element);    else        linkBefore(element, node(index));}

linkBefore方法传一个node(index)方法。该方法就是找到index位置的Node节点。如果 index

addFirst

因为是双端队列,除了前面提到的队尾插入还有队头插入元素方法,内部调用的linkFirst方法,其实与linkLast类似。

unlink*方法分析

除了前面介绍的link*方法,还有三个unlink* 方法,作用是干嘛呢?link*是插入元素到列表,unlink其实是从链表删除一个节点意思。

  1. unlinkFirst:删除队头的节点。
  2. unlinkLast:删除队尾的节点。
  3. unlink :删除中间的节点

队列删除

队列有好几个remove方法 内部都是调用了unlink方法。

get

getFirst 和getLast都很快。
get(index) 内部调用的node(index)方法定位的,前面已经提到过。

indexof,contains方法分析

contains调用了indexOf,
indexof是从头开始查找,lastIndexOf从尾部开始查找,我们看下indexOf方法:

public int indexOf(Object o) {    int index = 0;    if (o == null) {        for (Node<E> x = first; x != null; x = x.next) {            if (x.item == null)                return index;            index++;        }    } else {        for (Node<E> x = first; x != null; x = x.next) {            if (o.equals(x.item))                return index;            index++;        }    }    return -1;}

方法也是循环比较节点的内容和当前是否相等。

Deque 方法分析

Deque一般我们用的比较少。

Deque是一个线性集合,允许你在头部和尾部插入和取出数据。

LinkedList实现了Deque接口,并且他是一个非阻塞的Deque。

下面我们分析下Deque的方法实现。

这里写图片描述

add几个方法已经见过,get也见过,我们主要分析Deque几个不怎么熟悉的方法。

offer*

offer也是增加元素的意思,类似于add

offer和offerLast表示在尾部加入元素。,内部也是调用了add*方法。
offerFirst表示头部加入。下面是offerFirst代码:

 public boolean offerFirst(E e) {    addFirst(e);    return true;}

peek*

peek是查看的意思,这里也是查看队尾或者头的元素。

peek和peekFirst 代码完全一样。
peekLast查看队尾元素。

poll*

poll类似于peek,但是他会把对应额节点删除。

pop*

pop*和poll*差不多,只是pop空节点 会抛出异常。

push

加入元素 直接调用了addFirst。

removeFirstOccurrence等

removeFirstOccurrence内部直接调用了remove。

remove从头循环删除对应的一个元素。

removeLast当然是从队尾循环删除对应的一个元素。

0 0
原创粉丝点击