Java集合之List——LinkedList详解

Java集合之List——LinkedList详解

简介

  1. LinkedList的底层实现是一个双向链表,它是接口ListDeque的一个实现类;
  2. LinkedList非同步的;
  3. LinkedListiteratorlistIterator方法返回的迭代器是fail-fast的。

源码解读

继承关系

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

结构图
Java集合之List——LinkedList详解_第1张图片

ArrayList相比而言多实现了Deque接口,继承的抽象类变为了AbstractList的子类AbstractSequentialList,并且减少了RandomAccess接口的实现。

成员变量

仅介绍部分。
LinkedList的底层由一个双向链表实现,其定义为:

private static class Node<E> {
     
	E item; 	//节点存储的内容
	Node<E> next; //对下个节点的引用
	Node<E> prev; //对前个节点的引用
}

其余一些使用频率较高的成员变量:

transient int size = 0;  //集合大小
transient Node<E> first;//指向头结点的引用
transient Node<E> last;//指向尾结点的引用

一下两张图片帮助理解,来源
Java集合之List——LinkedList详解_第2张图片Java集合之List——LinkedList详解_第3张图片

构造方法

  1. public LinkedList();,无参构造方法;
  2. public LinkedList(Collection c);,构造一个包含指定集合元素的列表,其顺序由集合的迭代器返回。
public LinkedList() {
     
}
public LinkedList(Collection<? extends E> c) {
     
    this();
    addAll(c);  //将c中元素依次添加进集合
}

辅助方法

接下来将介绍一些辅助方法,比较重要LinkedList中通过其完成大部分ListDeque操作,如Add、remove、offer等方法。

private void linkFirst(E e) {
       //头插元素
    final Node<E> f = first;  //临时保存下当前头结点
    final Node<E> newNode = new Node<>(null, e, f); //创建新节点,并将新节点的next指向原头结点
    first = newNode; //该节点为新头结点
    if (f == null) //如果原头结点为null,则说明原集合为空,
        last = newNode; //插入后集合中仅有一个元素,头、尾节点指向同一节点
    else
        f.prev = newNode;//将原头结点的prev指向新头结点
    size++; //更新集合大小
    modCount++;
}

void linkLast(E e); //这个是尾插法,与头插法类似,不过多赘述

void linkBefore(E e, Node<E> succ) {
      //在制定位置succ之前插入节点,即固定位置插入节点
    // assert succ != null;
    final Node<E> pred = succ.prev; //临时保存succ.prev插入之前指向的节点
    final Node<E> newNode = new Node<>(pred, e, succ);
    succ.prev = newNode; //断开原连接
    if (pred == null) //说明此时为头插
        first = newNode;
    else
        pred.next = newNode; //恢复连接
    size++;
    modCount++;
}
private E unlinkFirst(Node<E> f) {
      //删除头结点
    // assert f == first && f != null;
    final E element = f.item; //存下要删节点的内容,即方法返回值
    final Node<E> next = f.next; //新头结点
    f.item = null; //删除节点
    f.next = null; // help GC
    first = next;
    if (next == null) //删除后集合为空
        last = null;
    else
        next.prev = null; //否则头结点的prev为null
    size--;//更新集合大小
    modCount++;
    return element;
}

private E unlinkLast(Node<E> l);//删除尾结点,与上述方法类似

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; //赋值新头结点
    } 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;
}
Node<E> node(int index) {
      //用于定位要操作节点的位置
    // assert isElementIndex(index);
    if (index < (size >> 1)) {
       //比较查询位置位于前半段还是后半段,加速查询
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
     
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

常用方法

ArrayList很接近(不考虑实现的话),具体可见博客
这里主要介绍一下与ArrayL不同的,即Deque接口的一些实现方法。
以下两组都可以将集合作为队列来操作,两组操作的区别:

  1. 前者属于List的实现,后者属于queue的实现;
  2. 后者offer、poll、peek相对比较友好,遇到队列为空等情况不会报错,而是有返回值,而前一组抛出错误
  3. 作为List使用时,一般采用add、get方法,作为Queue使用时,才会采用 offer、poll、peek等方法;
  4. 作为链表对象时,offer等方法相对来说没有什么意义。
boolean add(E e); //将元素插入队列

E remove();//将队首的元素删除,队列为空则抛出异常

E element();//获取队首元素,但不移除,队列为空则抛出异常
boolean offer(E e);//将元素插入队列

E poll();//将队首的元素删除,队列为空则返回null

E peek();//获取队首元素,但不移除,队列为空则返回null

其余操作,可相互组合,当做队列使用。

public boolean offerFirst(E e); //存
public boolean offerLast(E e);
public E peekFirst(); //取
public E peekLast();
public E pollFirst(); //删
public E pollLast();

//还有一对操作push、pop,可直接当做栈来使用
public void push(E e) {
     
	addFirst(e);
}
public E pop() {
     
    return removeFirst();
}

效率分析

LinkedList由于本质是链表,相对于ArrayList而言,插入或删除元素时不再需要对元素进行挪动,所以它在数据的删改方面会很快(只指插入删除这一个动作),而在查询或修改元素过程时,不能像数组一下通过下标访问,每次需要遍历链表进行查找,这样就比ArrayList慢很多。

综上LinkedList,在增删(在List前段位置)操作较多,查询、修改较少时比较合适。

你可能感兴趣的:(Collection,Java,List,java)