Java集合源码分析之LinkedList

1. LinkedList简介

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

可以看到LinkedList类继承AbstractSequentialList类,实现了List, Deque, Cloneable, java.io.Serializable接口。实现List接口,实现对列表的增删改查操作,并且元素可以为null,实现Deque接口,为add,poll提供先进先出队列操作,以及其他堆栈和双端队列操作。LinkedList是不同步的,多线程不安全,迭代器是快速失败的(fail-fast),不能再迭代过程中对集合进行修改操作。

2. 成员变量

//集合的元素个数
transient int size = 0;
//头节点
transient Node<E> first;
//尾节点
transient Node<E> last;

LinkedList的成员变量很简单只有三个集合:size元素个数,first头节点,last尾节点。

3. 构造方法

空参构造方法:

public LinkedList() {
}

传入一个集合:

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

可以看到调用了addAll()方法将传入集合的所有元素添加到了LinkedList中。

4. 内部方法

4.1 linkFirst(E e)将e链接到链表的头
private void linkFirst(E e) {
    //保存当前集合的头节点到f
    final Node<E> f = first;
    //e保存到newNode中,关于  Node<E>的源码分析在后面
    final Node<E> newNode = new Node<>(null, e, f);
    //头节点赋为newNode
    first = newNode;
    //判断原集合是否为空(判断原头节点是否为null)
    if (f == null)
        //如果原集合为空,将集合的last尾节点也赋为newNode
        last = newNode;
    else
        //如果原集合不为空(first不为null),将原头节点的前驱节点赋为newNode
        f.prev = newNode;
    //结合长度自加
    size++;
    //对集合做出修改modCount自加保证集合版本一致
    modCount++;
}
4.2 linkLast(E e)将e链接到链表的尾
void linkLast(E e) {
    //保存当前集合的尾节点到l
    final Node<E> l = last;
    //e保存到newNode中
    final Node<E> newNode = new Node<>(l, e, null);
    //尾节点赋为newNode
    last = newNode;
    //判断尾节点是否为null(集合是否为空)
    if (l == null)
        //如果集合是空,头节点赋为newNode  first=last
        first = newNode;
    else
        //集合不为空,将新的节点追加到原集合的last尾节点后作为新的尾节点
        l.next = newNode;
    //集合个数自加
    size++;
    //对集合做出修改modCount自加保证集合版本一致
    modCount++;
}
4.3 linkBefore(E e, Node succ)在一个非空节点前插入元素
void linkBefore(E e, Node<E> succ) {
    //取得要插入的节点的前驱节点pred
    final Node<E> pred = succ.prev;
    //插入节点的前驱节点为pred,后继节点为当前节点succ
    final Node<E> newNode = new Node<>(pred, e, succ);
    //succ的前驱节点赋为新节点
    succ.prev = newNode;
    //判断succ即要插入的位置的节点的前驱节点是否为null
    if (pred == null)
        //如果为空那么原节点就是头节点,所以将新节点置为头节点
        first = newNode;
    else
        //如果不为空,succ不是集合的头节点,那么将前驱节点的后继节点置为新节点
        pred.next = newNode;
    //集合个数自加
    size++;
    //对集合做出修改modCount自加保证集合版本一致
    modCount++;
}
4.4 unlinkFirst(Node f)解除头节点并且返回该节点保存的值
private E unlinkFirst(Node<E> f) {
    //取出头节点的值存到element中
    final E element = f.item;
    //将头节点的后继节点存到next中
    final Node<E> next = f.next;
    //将头节点的值和下个节点的地址值置为null
    f.item = null;
    f.next = null; // help GC
    //原头节点的后继节点置为集合的头节点
    first = next;
    //判断原头节点的后继节点是否为空
    if (next == null)
        //如果为空,那么原集合就只有一个节点,那么尾节点置为空
        last = null;
    else
        //不为空,将新的头节点的前驱节点置为null
        next.prev = null;
    //集合长度自减
    size--;
    //对集合做出修改modCount自加保证集合版本一致
    modCount++;
    //返回原头节点保存的数据
    return element;
}
4.5 unlinkLast(Node l)移除尾节点并将尾节点保存的数据返回
private E unlinkLast(Node<E> l) {
    //取出位节点保存的数据
    final E element = l.item;
    //取出尾节点的前驱节点
    final Node<E> prev = l.prev;
    //尾节点保存数据置为null
    l.item = null;
    //尾节点的前驱节点置为null
    l.prev = null; // help GC
     //原尾节点的前驱节点置为当前结合的尾节点
    last = prev;
    //判断原尾节点的前驱节点是否为null
    if (prev == null)
        //如果是那么头节点置为null,集合为空
        first = null;
    else
        //否,那么原尾节点的前驱节点的后继节点置为null
        prev.next = null;
    //集合长度自减
    size--;
    //对集合做出修改modCount自加保证集合版本一致
    modCount++;
    //返回原尾节点保存的数据
    return element;
}
4.6 nulik(Node x)移除一个非空节点
E unlink(Node<E> x) {
    //取出要移除节点保存的数据
    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;
        //移除节点的后继节点置为null
        x.prev = null;
    }
    //移除节点的后继节点为null即要移除的节点为集合的尾节点
    if (next == null) {
        //将移除节点的前驱节点值为集合的尾节点
        last = prev;
    } else {
        //将原节点的前驱节点置为后继节点的前驱节点
        next.prev = prev;
        //移除节点的后继节点置为null
        x.next = null;
    }
    //移除节点保存的数据置为null
    x.item = null;
    //集合长度自减
    size--;
    //对集合做出修改modCount自加保证集合版本一致
    modCount++;
    //返回移除节点保存的数据
    return element;
}
4.7 isElementIndex(int index) 判断指定索引位置是否为元素索引位置
private boolean isElementIndex(int index) {
    //判断index是否在0到size之间 包括0 但是不包括size
    return index >= 0 && index < size;
}
4.8 isPositionIndex(int index) 判断指定索引位置是否为可用位置(对于迭代器和add方法)
private boolean isPositionIndex(int index) {
    return index >= 0 && index <= size;
}
4.9 node(int index) 返回指定位置的节点
Node<E> node(int index) {
    //判断传入的index是否在集合的前半部分
    if (index < (size >> 1)) {
        Node<E> x = first;
        //从0开始遍历到到指定的index处
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        //如果index在集合的后半部分
        Node<E> x = last;
        //倒着遍历集合
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

尽管知道索引index,但是由于LinkedList没有实现RandomAccess接口,所以不能依靠索引快速查询,只能通过遍历的方法,node通过先判断index是在前半部分还是在后半部分,将遍历的时间复杂度从O(n)降低到了O(n/2)但是对于数据庞大的集合,这样的查找效率还是太低了。

5. 查找方法

5.1 getFirst()获取集合的头节点
public E getFirst() {
    //将集合头节点取出
    final Node<E> f = first;
    //判断头节点是否为空
    if (f == null)
        throw new NoSuchElementException();
    //返回头节点的数据
    return f.item;
}
5.2 getLat()获取集合的尾节点
public E getLast() {
    //取出集合的尾节点
    final Node<E> l = last;
    //尾节点是否为空
    if (l == null)
        throw new NoSuchElementException();
    //返回尾节点的数据
    return l.item;
}
5.3 contains()是否包含某个元素
public boolean contains(Object o) {
    return indexOf(o) >= 0;
}
5.4 size返回集合长度
public int size() {
    return size;
}
5.5 indexOf(Object o) 返回指定数据的索引
public int indexOf(Object o) {
    //index初始化index
    int index = 0;
    //如果传入o为null
    if (o == null) {
        //遍历集合找到为村川数据为null的元素返回其索引
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null)
                return index;
            index++;
        }
    } else {
        //遍历集合找到存储数据和传入的o相同的元素返回其索引
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item))
                return index;
            index++;
        }
    }
    //没有找到返回-1
    return -1;
}

##### 5.6 lastIndexOf(Object o)返回指定数据最后出现的索引

public int lastIndexOf(Object o) {
    //倒着遍历,所以初始化index为size
    int index = size;
    //如果穿的数据为null,倒着遍历找到数据为null的节点
    if (o == null) {
        for (Node<E> x = last; x != null; x = x.prev) {
            index--;
            if (x.item == null)
                return index;
        }
    } else {
        //倒着遍历找到数据为o的节点
        for (Node<E> x = last; x != null; x = x.prev) {
            index--;
            if (o.equals(x.item))
                return index;
        }
    }
    //没有找到返回-1
    return -1;
}
5.7 peek() 返回头节点
public E peek() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}
5.8 element() 返回头节点
public E element() {
    return getFirst();
}
5.9 peekFirst() 返回头节点不删除
public E peekFirst() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
 }
5.10 peekLast() 返回尾节点不删除
public E peekLast() {
    final Node<E> l = last;
    return (l == null) ? null : l.item;
}

6. 增删方法

6.1 removeFirst()删除头节点
public E removeFirst() {
    //取出头节点
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    //直接调用unlikFirst方法删除头节点
    return unlinkFirst(f);
}
6.2 removeLast()删除尾节点
public E removeLast() {
    //取出尾节点
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    //调用unlinkLast方法删除尾节点
    return unlinkLast(l);
}
6.3 addFirst()在集合头部插入节点
public void addFirst(E e) {
    //直接调用linkFirst方法添加头节点
    linkFirst(e);
}
6.4 addLast()在集合尾部添加节点
public void addLast(E e) {
    //调用linkLast添加节点
    linkLast(e);
}
6.5 add()集合尾部追加一个元素
public boolean add(E e) {
    //调用linkLast方法在更换尾节点
    linkLast(e);
    return true;
}
6.6 remove()移除集合中第一次出现的某个数据的节点
public boolean remove(Object o) {
    //判断传入数据是否为null
    if (o == null) {
        //遍历集合
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null) {
                //找到元素,用unlink方法删除
                unlink(x);
                return true;
            }
        }
    } else {
        //遍历集合
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                //找到元素,用unlink方法删除
                unlink(x);
                return true;
            }
        }
    }
    //没有找到这个元素返回false
    return false;
}
6.7 addAll()在集合尾部添加指定集合
public boolean addAll(Collection<? extends E> c) {
    return addAll(size, c);
}
6.8 addAll(int index, Collection<? extends E> c)向指定位置添加集合
public boolean addAll(int index, Collection<? extends E> c) {
    //检查下标是否越界
    checkPositionIndex(index);
    //将传入的c转化为数组
    Object[] a = c.toArray();
    //获取数组的长度
    int numNew = a.length;
    //如果数组为空返回false
    if (numNew == 0)
        return false;
    //pred succ节点  index节点
    Node<E> pred, succ;
    //如果插入的索引位置为集合尾部
    if (index == size) {
        //succ节点置null
        succ = null;
        //将集合的尾节点赋给pred
        pred = last;
    } else {
        //如果不是在尾部插入指定的集合,那么用node方法找到要插入位置的节点
        //赋给succ
        succ = node(index);
        //pred就为succ的前驱节点
        pred = succ.prev;
    }

    //遍历数组
    for (Object o : a) {
        @SuppressWarnings("unchecked") E e = (E) o;
        //数组的值放置到节点中newNode前驱节点为pred
        Node<E> newNode = new Node<>(pred, e, null);
        //如果前驱节点为null
        if (pred == null)
            //将插入的节点置为集合头节点
            first = newNode;
        else
            //前驱节点不为null,将前驱节点的后继节点置为插入的节点
            pred.next = newNode;
        //pred置为插入的节点,进入下个循环
        pred = newNode;
    }
    //succ是否为null,说明在结合尾部添加的所以尾节点就是pred
    if (succ == null) {
        //将最后的新节点置为尾节点
        last = pred;
    } else {
        //否则是在集合中插入的 那么前驱节点的后继节点就是succ succ是index处的节点
        pred.next = succ;
        //插入的最后的一个节点为succ的前驱节点
        succ.prev = pred;
    }
    //集合个数增加插入节点个数
    size += numNew;
    //对集合做出修改modCount自加保证集合版本一致
    modCount++;
    return true;
}
6.9 clear() 清除所有节点
public void clear() {
    //遍历所有节点将所有节点前驱节点,保存数据,后继节点都置为null
    for (Node<E> x = first; x != null; ) {
        Node<E> next = x.next;
        x.item = null;
        x.next = null;
        x.prev = null;
        x = next;
    }
    //头节点和尾节点置为null
    first = last = null;
    //集合长度为0
    size = 0;
    //对集合做出修改modCount自加保证集合版本一致
    modCount++;
}
6.10 get(int index) 获取指定索引处的节点
public E get(int index) {
    //检查index是否在0到size范围
    checkElementIndex(index);
    //调用node方法获取index处的节点的数据
    return node(index).item;
}
6.11 set(int index, E element) 对指定index处的节点修改
public E set(int index, E element) {
    //检查index是否在0到size范围
    checkElementIndex(index);
    //取得index处的节点
    Node<E> x = node(index);
    //取得原index处节点的数据
    E oldVal = x.item;
    //index处的节点数据置为element
    x.item = element;
    //将index处的原数据返回
    return oldVal;
}
6.12 add(int inedx, E element) 在指定索引位置添加数据
public void add(int index, E element) {
    //检查index是否在0到size范围内
    checkPositionIndex(index);
    //如果index等于size,那么在集合尾添加
    if (index == size)
        linkLast(element);
    else
        //调用linkBefore方法,指定位置前添加节点
        linkBefore(element, node(index));
}
6.13 remove(int index) 移除指定索引处的节点
public E remove(int index) {
    //检查index是否在0到size范围内
    checkElementIndex(index);
    //调用unLink方法解除一个节点
    return unlink(node(index));
}
6.14 poll() 返回头节点并且删除
public E poll() {
    final Node<E> f = first;
    //判断头节点是否为null,如果为null的话直接返回null,不是的话调用nulinkFirst方法删除并返回头节点
    return (f == null) ? null : unlinkFirst(f);
}
6.15 remove() 删除头节点并返回删除的头节点
public E remove() {
    return removeFirst();
}
6.16 offer() 在集合尾部添加元素
public boolean offer(E e) {
    return add(e);
}
6.17 offerFirst() 添加头节点
public boolean offerFirst(E e) {
    addFirst(e);
    return true;
}
6.18 offerLast() 添加尾节点
public boolean offerLast(E e) {
    addLast(e);
    return true;
}
6.20 pollFirst() 返回并移除头节点
public E pollFirst() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}
6.21 pollLast() 返回并删除尾节点
public E pollLast() {
    final Node<E> l = last;
    return (l == null) ? null : unlinkLast(l);
}
6.22 push() 将元素插入到头节点
public void push(E e) {
    addFirst(e);
}
6.23 pop() 移除头节点并返回
public E pop() {
    return removeFirst();
}
6.24 removeFirstOccurrence() 移除第一次出现的某个元素
public boolean removeFirstOccurrence(Object o) {
    return remove(o);
}
6.25 removeLastOccurrence() 移除最后一次出现的某个元素
public boolean removeLastOccurrence(Object o) {
    //当要删除的元素为null
    if (o == null) {
        //从尾节点向前遍历集合
        for (Node<E> x = last; x != null; x = x.prev) {
            if (x.item == null) {
                //调用nulink方法删除节点
                unlink(x);
                return true;
            }
        }
    } else {
        //从后向前遍历集合
        for (Node<E> x = last; x != null; x = x.prev) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    //没有找到这个元素返回false
    return false;
}

7. 总结

7.1 底层实现

LinkedList底层是通过双向链表来实现的

7.2 线程安全?

LinkedList线程不安全。

7.3 增删和查找效率

对于双向链表来说增删效率很高,对于单向链表删除某个节点还需要从头遍历来说,效率高很多,对于要操作的节点直接使用unlink()方法就可以实现删除。而对于查找来说,由于LinkedList没有索引,所以无法快速获取某个节点,必须要遍历,尽管遍历的时候已经可以降低到O(N/2),但是相比数组直接通过索引快速获取来说还是效率低了很多。因此,需要经常插入增删的数据采用LinkedList保存更好。

7.4 LinkedList与ArrayList
  • ArrayList使用可变数组实现,LinkedList底层是通过双向链表来实现的。
  • 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
  • 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
  • ArrayList适合用于需要频繁查找和修改的环境,LinkedList则适合用于需要频繁增删的环境。

关于ArrayList的源码分析可以看这里

原文地址:https://www.cnblogs.com/Linteresting/p/10776365.html

时间: 2024-10-07 06:34:22

Java集合源码分析之LinkedList的相关文章

Java集合源码分析之 LinkedList

一.简介 LinkedList是一个常用的集合类,用于顺序存储元素.LinkedList经常和ArrayList一起被提及.大部分人应该都知道ArrayList内部采用数组保存元素,适合用于随机访问比较多的场景,而随机插入.删除等操作因为要移动元素而比较慢.LinkedList内部采用链表的形式存储元素,随机访问比较慢,但是插入.删除元素比较快,一般认为时间复杂都是O(1)(需要查找元素时就不是了,下面会说明).本文分析LinkedList的具体实现. 二.数据结构 如上图所示,LinkedLi

Java集合源码分析(三)LinkedList

LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使用. LinkedList同样是非线程安全的,只在单线程下适合使用. LinkedList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了Cloneable接口,能被克隆. LinkedList源码 以下是linkedList源码(加入简单代码注释): /* * @(#)LinkedList.java 1.6

【Java集合源码剖析】LinkedList源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/35787253 LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使用. LinkedList同样是非线程安全的,只在单线程下适合使用. LinkedList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了Cloneable接口,能被克隆. Linked

Java 集合源码分析(一)HashMap

目录 Java 集合源码分析(一)HashMap 1. 概要 2. JDK 7 的 HashMap 3. JDK 1.8 的 HashMap 4. Hashtable 5. JDK 1.7 的 ConcurrentHashMap 6. JDK 1.8 的 ConcurrentHashMap 7. 最后补充一下 HashMap 中的一些属性和方法 附:更这个系列感觉自己像是又挖了一个坑??,不过趁自己刚好工作不太忙,有空闲期,静下心来研究学习源码也是一件很值得做的事,自己尽量会把这个坑填完??.

Java集合源码分析(四)Vector&lt;E&gt;

Vector<E>简介 Vector也是基于数组实现的,是一个动态数组,其容量能自动增长. Vector是JDK1.0引入了,它的很多实现方法都加入了同步语句,因此是线程安全的(其实也只是相对安全,有些时候还是要加入同步语句来保证线程的安全),可以用于多线程环境. Vector没有丝线Serializable接口,因此它不支持序列化,实现了Cloneable接口,能被克隆,实现了RandomAccess接口,支持快速随机访问. Vector<E>源码 如下(已加入详细注释): /*

Java集合源码分析(二)ArrayList

ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类. ArrayList实现了Serializable接口,因此它支持序列化,能够通过

Java集合源码分析(七)HashMap&lt;K, V&gt;

一.HashMap概述 HashMap基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了不同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同.)此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 值得注意的是HashMap不是线程安全的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap. Map map = Coll

转:【Java集合源码剖析】LinkedList源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/35787253   您好,我正在参加CSDN博文大赛,如果您喜欢我的文章,希望您能帮我投一票,谢谢! 投票地址:http://vote.blog.csdn.net/Article/Details?articleid=35568011 LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使

Java集合源码分析之LikedList

一.LinkedList结构 LinkedList是一种可以在任何位置进行高效地插入和移除操作的有序序列,它是基于双向链表实现的. LinkedList 是一个继承于AbstractSequentialList的双向链表.它也可以被当作堆栈.队列或双端队列进行操作. LinkedList 实现 List 接口,能对它进行队列操作. LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用. LinkedList 实现了Cloneable接口,即覆盖了函数clone