zoukankan      html  css  js  c++  java
  • Java集合框架之LinkedList浅析

    Java集合框架之LinkedList浅析

    一、LinkedList综述:

    1.1LinkedList简介

    • 同ArrayList一样,位于java.util包下的LinkedList是Java集合框架的重要组成成员之一,LinkedList在jdk1.8中的定义如下:public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
    • LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
    • LinkedList 实现 List 接口,能对它进行队列操作。
    • LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
    • LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
    • LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
    • LinkedList 是非同步的。

    1.2LinkedList的数据结构

    LinkedList的继承关系

    java.lang.Object
       ↳     java.util.AbstractCollection<E>
             ↳     java.util.AbstractList<E>
                   ↳     java.util.AbstractSequentialList<E>
                         ↳     java.util.LinkedList<E>
    
    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable {}

    LinkedList与Collection关系如下图:

    LinkedList的本质是双向链表。
    (01) LinkedList继承于AbstractSequentialList,并且实现了Dequeue接口。
    (02) LinkedList包含两个重要的成员:header 和 size。
      header是双向链表的表头,它是双向链表节点所对应的类Entry的实例。Entry中包含成员变量: previous, next, element。其中,previous是该节点的上一个节点,next是该节点的下一个节点,element是该节点所包含的值。
      size是双向链表中节点的个数。

    (03) LinkedList 实际上是通过双向链表去实现的。

      它包含一个非常重要的内部类:Entry。Entry是双向链表节点所对应的数据结构,它包括的属性有:当前节点所包含的值,上一个节点,下一个节点。
    (04) 从LinkedList的实现方式中可以发现,它不存在LinkedList容量不足的问题。
    (05) LinkedList的克隆函数,即是将全部元素克隆到一个新的LinkedList对象中。
    (06) LinkedList实现java.io.Serializable。当写入到输出流时,先写入“容量”,再依次写入“每一个节点保护的值”;当读出输入流时,先读取“容量”,再依次读取“每一个元素”。
    (07) 由于LinkedList实现了Deque,而Deque接口定义了在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null 或 false,具体取决于操作)。

    总结起来如下表格:

            第一个元素(头部)                 最后一个元素(尾部)
            抛出异常        特殊值            抛出异常        特殊值
    插入    addFirst(e)    offerFirst(e)    addLast(e)        offerLast(e)
    移除    removeFirst()  pollFirst()      removeLast()    pollLast()
    检查    getFirst()     peekFirst()      getLast()        peekLast()

    (08) LinkedList可以作为FIFO(先进先出)的队列,作为FIFO的队列时,下表的方法等价:

    队列方法       等效方法
    add(e)        addLast(e)
    offer(e)      offerLast(e)
    remove()      removeFirst()
    poll()        pollFirst()
    element()     getFirst()
    peek()        peekFirst()

    (09) LinkedList可以作为LIFO(后进先出)的栈,作为LIFO的栈时,下表的方法等价:

    栈方法        等效方法
    push(e)      addFirst(e)
    pop()        removeFirst()
    peek()       peekFirst()

    二、LinkedList与ArrayList对比:

    1、顺序插入速度ArrayList会比较快,因为ArrayList是基于数组实现的,数组是事先new好的,只要往指定位置塞一个数据就了;LinkedList则不同,每次顺序插入的时候LinkedList将new一个对象出来,如果对象比较大,那么new的时间势必会长一点,再加上一些引用赋值的操作,所以顺序插入LinkedList必然慢于ArrayList

    2、基于上一点,因为LinkedList里面不仅维护了待插入的元素,还维护了Entry的前置Entry和后继Entry,如果一个LinkedList中的Entry非常多,那么LinkedList将比ArrayList更耗费一些内存

    3、数据遍历的速度,使用各自遍历效率最高的方式,ArrayList的遍历效率会比LinkedList的遍历效率高一些

    4、有些说法认为LinkedList做插入和删除更快,这种说法其实是不准确的:

    (1)LinkedList做插入、删除的时候,慢在寻址,快在只需要改变前后Entry的引用地址

    (2)ArrayList做插入、删除的时候,慢在数组元素的批量copy,快在寻址

      所以,如果待插入、删除的元素是在数据结构的前半段尤其是非常靠前的位置的时候,LinkedList的效率将大大快过ArrayList,因为ArrayList将批量copy大量的元素;越往后,对于LinkedList来说,因为它是双向链表,所以在第2个元素后面插入一个数据和在倒数第2个元素后面插入一个元素在效率上基本没有差别,但是ArrayList由于要批量copy的元素越来越少,操作速度必然追上乃至超过LinkedList。

      从这个分析看出,如果你十分确定你插入、删除的元素是在前半段,那么就使用LinkedList;如果你十分确定你删除、删除的元素在比较靠后的位置,那么可以考虑使用ArrayList。如果你不能确定你要做的插入、删除是在哪儿呢?那还是建议你使用LinkedList吧,因为一来LinkedList整体插入、删除的执行效率比较稳定,没有ArrayList这种越往后越快的情况;二来插入元素的时候,弄得不好ArrayList就要进行一次扩容,记住,ArrayList底层数组扩容是一个既消耗时间又消耗空间的操作

      最后一点,一切都是纸上谈兵,在选择了List后,有条件的最好可以做一些性能测试,比如在你的代码上下文记录List操作的时间消耗。

    三、LinkedList方法摘要:

    构造方法摘要
    LinkedList()
              构造一个空列表。
    LinkedList(Collection<? extends E> c)
              构造一个包含指定 collection 中的元素的列表,这些元素按其 collection 的迭代器返回的顺序排列。
    方法摘要
     boolean add(E e)
              将指定元素添加到此列表的结尾。
     void add(int index, E element)
              在此列表中指定的位置插入指定的元素。
     boolean addAll(Collection<? extends E> c)
              添加指定 collection 中的所有元素到此列表的结尾,顺序是指定 collection 的迭代器返回这些元素的顺序。
     boolean addAll(int index, Collection<? extends E> c)
              将指定 collection 中的所有元素从指定位置开始插入此列表。
     void addFirst(E e)
              将指定元素插入此列表的开头。
     void addLast(E e)
              将指定元素添加到此列表的结尾。
     void clear()
              从此列表中移除所有元素。
     Object clone()
              返回此 LinkedList 的副本。
     boolean contains(Object o)
              如果此列表包含指定元素,则返回 true
     Iterator<E> descendingIterator()
              返回以逆向顺序在此双端队列的元素上进行迭代的迭代器。
     E element()
              获取但不移除此列表的头(第一个元素)。
     E get(int index)
              返回此列表中指定位置处的元素。
     E getFirst()
              返回此列表的第一个元素。
     E getLast()
              返回此列表的最后一个元素。
     int indexOf(Object o)
              返回此列表中首次出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1。
     int lastIndexOf(Object o)
              返回此列表中最后出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1。
     ListIterator<E> listIterator(int index)
              返回此列表中的元素的列表迭代器(按适当顺序),从列表中指定位置开始。
     boolean offer(E e)
              将指定元素添加到此列表的末尾(最后一个元素)。
     boolean offerFirst(E e)
              在此列表的开头插入指定的元素。
     boolean offerLast(E e)
              在此列表末尾插入指定的元素。
     E peek()
              获取但不移除此列表的头(第一个元素)。
     E peekFirst()
              获取但不移除此列表的第一个元素;如果此列表为空,则返回 null
     E peekLast()
              获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null
     E poll()
              获取并移除此列表的头(第一个元素)
     E pollFirst()
              获取并移除此列表的第一个元素;如果此列表为空,则返回 null
     E pollLast()
              获取并移除此列表的最后一个元素;如果此列表为空,则返回 null
     E pop()
              从此列表所表示的堆栈处弹出一个元素。
     void push(E e)
              将元素推入此列表所表示的堆栈。
     E remove()
              获取并移除此列表的头(第一个元素)。
     E remove(int index)
              移除此列表中指定位置处的元素。
     boolean remove(Object o)
              从此列表中移除首次出现的指定元素(如果存在)。
     E removeFirst()
              移除并返回此列表的第一个元素。
     boolean removeFirstOccurrence(Object o)
              从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表时)。
     E removeLast()
              移除并返回此列表的最后一个元素。
     boolean removeLastOccurrence(Object o)
              从此列表中移除最后一次出现的指定元素(从头部到尾部遍历列表时)。
     E set(int index, E element)
              将此列表中指定位置的元素替换为指定的元素。
     int size()
              返回此列表的元素数。
     Object[] toArray()
              返回以适当顺序(从第一个元素到最后一个元素)包含此列表中所有元素的数组。
    <T> T[]
    toArray(T[] a)
              返回以适当顺序(从第一个元素到最后一个元素)包含此列表中所有元素的数组;返回数组的运行时类型为指定数组的类型。

     参考:

      https://www.cnblogs.com/skywang12345/p/3308807.html#a1

      https://www.cnblogs.com/xrq730/p/5005347.html

        

  • 相关阅读:
    第九章 引用
    第八章 指针
    第六章 面向对象
    第五章 if语句与运算符
    第四章 C++数据类型
    第三章 初步了解函数
    第二章 做一个简短的C++程序
    第一章 初始C++
    vs2012 快捷键+方法
    vue如何修改生效日期范围,以及转化成yyyy-mm-dd的格式
  • 原文地址:https://www.cnblogs.com/albertrui/p/8331331.html
Copyright © 2011-2022 走看看