【JAVA集合】LinkedList

以下内容基于jdk1.7.0_79源码;

什么是LinkedList

List接口的链表实现,并提供了一些队列,栈,双端队列操作的方法;

LinkedList补充说明

与ArrayList对比,LinkedList插入和删除操作更加高效,随机访问速度慢;

可以作为栈、队列、双端队列数据结构使用;

非同步,线程不安全;

与ArrayList、Vector一样,LinkedList的内部迭代器存在“快速失败行为”;

支持null元素、有顺序、元素可以重复;

LinkedList继承的类以及实现的接口

以上接口和类中,关于Iterable接口、Collection接口、List接口、 Cloneable、 java.io.Serializable接口、AbstractCollection类、AbstractList类的相关说明,在介绍ArrayList的时候,已经有了个大概说明,这里将主要了解下Queue接口、Deque接口、AbstractSequentialList类以及LinkedList类;

Queue接口

队列接口,定义了一些队列的基本操作,

注意使用时优先选择以下蓝色字体方法(offer、poll、peek),队列通常不允许null元素,因为我们通常调用poll方法是否返回null来判断队列是否为空,但是LinkedList是允许null元素的,所以,在使用LinkedList最为队列的实现时,不应该将null元素插入队列;

boolean add(E e);

将对象e插入队列尾部,成功返回true,失败(没有空间)抛出异常IllegalStateException

boolean offer(E e);

将对象e插入队列尾部,成功返回true,失败(没有空间)返回false;

E remove();

获取并移除队列头部元素,如果队列为空,抛出NoSuchElementException异常;

E poll();

获取并移除队列头部元素,如果队列为空,返回null;

E element();

获取但不移除队列头部元素,如果队列为空,抛出NoSuchElementException异常;

E peek();

获取但不移除队列头部元素,如果队列为空,返回null;

举个简单的例子,基于LinkedList实现的队列,代码如下:

package com.pichen.basis.col;

import java.util.LinkedList;
import java.util.Queue;

public class LinkListTest {

    public static void main(String[] args) {
        Queue<Integer> linkedListQueue = new LinkedList<Integer>();

        //入队
        linkedListQueue.offer(3);
        linkedListQueue.offer(4);
        linkedListQueue.offer(2);
        linkedListQueue.offer(1);

        //出队
        Integer tmp;
        while((tmp = linkedListQueue.poll()) != null){
            System.out.println(tmp);
        }

        System.out.println(linkedListQueue.peek());
    }
}

Deque接口

双端队列接口,继承队列接口,支持在队列两端进行入队和出队操作;

除了Collection接口Queue接口中定义的方法外,Deque还包括以下方法

void addFirst(E e);

将对象e插入到双端队列头部,容间不足时,抛出IllegalStateException异常;

void addLast(E e);

将对象e插入到双端队列尾部,容间不足时,抛出IllegalStateException异常;

boolean offerFirst(E e);

将对象e插入到双端队列头部

boolean offerLast(E e);

将对象e插入到双端队列尾部;

E removeFirst();

获取并移除队列第一个元素,队列为空,抛出NoSuchElementException异常;

E removeLast();

获取并移除队列最后一个元素,队列为空,抛出NoSuchElementException异常;

E pollFirst();

获取并移除队列第一个元素,队列为空,返回null;

E pollLast();

获取并移除队列最后一个元素,队列为空,返回null;

E getFirst();

获取队列第一个元素,但不移除,队列为空,抛出NoSuchElementException异常;

E getLast();

获取队列最后一个元素,但不移除,队列为空,抛出NoSuchElementException异常;

E peekFirst();

获取队列第一个元素,队列为空,返回null;

E peekLast();

获取队列最后一个元素,队列为空,返回null;

boolean removeFirstOccurrence(Object o);

移除第一个满足 (o==null ? e==null : o.equals(e)) 的元素

boolean removeLastOccurrence(Object o);

移除最后一个满足 (o==null ? e==null : o.equals(e)) 的元素

void push(E e);

将对象e插入到双端队列头部;

E pop();

移除并返回双端队列的第一个元素

Iterator<E> descendingIterator();

双端队列尾部到头部的一个迭代器;

AbstractSequentialList类

一个抽象类,基于迭代器实现数据的随机访问,以下方法的含义, 之前也说过,简单地说,就是数据的随机存取(利用了一个索引index);

public E get(int index)

public E set(int index, E element)

public void add(int index, E element)

public E remove(int index)

public boolean addAll(int index, Collection<? extends E> c)

LinkedList类

LinkedList的具体实现,

LinkedList中有两个关键成员属性,队头结点和队尾结点:

transient Node<E> first;  //队头节点

transient Node<E> last;  //队尾节点

LinkedList的节点内部类

具体代码如下,每个节点包含上一个节点的引用、下一个节点的引用以及该节点引用的具体对象;

    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;
        }
    }

至于LinkedList提供的每个方法的含义,在前面队列、双端队列、集合等接口中都有说明了,这里简单的举一两个方法的具体实现,对照源码了解下,其实就是链表的操作:

poll方法,出队操作

    public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }
    /**
     * Unlinks non-null first node f.
     */
    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;
        size--;
        modCount++;
        return element;
    }

获取并移除双端队列头部元素,如上代码,主要实现在unlinkFirst方法内,首先直接获取被删节点,临时存储其具体引用的对象element和下个引用next,然后将被删节点对象引用和下个节点引用置null给gc回收,改变双端队列队头结点为被删节点的下个引用next,如果next为空,将双端队列的队尾结点last置null,否则将next节点的前引用置null;队列长度减减,操作次数加加(快速失败机制),返回被删节点引用的具体对象;

public E get(int index)方法,随机访问方法

    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
    /**
     * Returns the (non-null) Node at the specified element index.
     */
    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;
        }
    }

LinkedList随机访问性能较差,首先是判断索引index合法性,然后调用node(int index)方法,在node方法中,做了一点优化,先判断要访问节点的索引是在队列的前半部分还是后半部分,如果在前半部分则从队头开始遍历,否则从队尾开始遍历,如上代码所示。

注意事项

适用场合很重要,注意和ArrayList区分开来,根据集合的各自特点以及具体场景,选择合适的List实现;

这里举个简单例子,分别使用ArrayList和LinkedList,测试下两个集合随机访问的性能,可以发现使用LinkedList随机访问的效率较ArrayList差很多;

package com.pichen.basis.col;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Stack;
import java.util.Vector;

public class Test {

    public static void main(String[] args) {
        //初始化linkedList和arrayList数据
        LinkedList<Integer> linkedList = new LinkedList<Integer>();
        for(int i = 0; i < 10000; i++){
            linkedList.offerLast(i);
        }

        List<Integer> arrayList = new ArrayList<Integer>();
        for(int i = 0; i < 10000; i++){
            arrayList.add(i);
        }

        long s, e;

        s = System.currentTimeMillis();
        for(int i = 0; i < 10000; i++){
            linkedList.get(i);
        }
        e = System.currentTimeMillis();
        System.out.println("linkedList:" + (e - s) + "ms");

        s = System.currentTimeMillis();
        for(int i = 0; i < 10000; i++){
            arrayList.get(i);
        }
        e = System.currentTimeMillis();
        System.out.println("arrayList:" + (e - s) + "ms");

    }
}

结果打印:

linkedList:56ms
arrayList:1ms
时间: 2024-10-22 16:59:53

【JAVA集合】LinkedList的相关文章

6.Java集合-LinkedList实现原理及源码分析

Java中LinkedList的部分源码(本文针对1.7的源码) LinkedList的基本结构 jdk1.7之后,node节点取代了 entry ,带来的变化是,将1.6中的环形结构优化为了直线型链表结构,从双向循环链表变成了双向链表 在LinkedList中,我们把链子的"环"叫做"节点",每个节点都是同样的结构.节点与节点之间相连,构成了我们LinkedList的基本数据结构,也是LinkedList的核心. 我们再来看一下LinkedList在jdk1.6和

Java集合--LinkedList

LinkedList 的本质是双向链表.实现 List 接口,能对它进行队列操作.实现 Deque 接口,能将LinkedList当作双端队列使用.LinkedList 是非同步的. LinkedList的继承关系 java.lang.Object ? java.util.AbstractCollection<E> ? java.util.AbstractList<E> ? java.util.AbstractSequentialList<E> ? java.util.

Java集合 LinkedList的原理及使用

LinkedList和ArrayList一样是集合List的实现类,虽然较之ArrayList,其使用场景并不多,但同样有用到的时候,那么接下来,我们来认识一下它. 一. 定义一个LinkedList public static void main(String[] args) { List<String> stringList = new LinkedList<>(); List<String> tempList = new ArrayList<>();

Java集合---LinkedList源码解析

一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据remove()7.数据获取get()8.数据复制clone()与toArray()9.遍历数据:Iterator()二.ListItr 一.源码解析 1. LinkedList类定义. public class LinkedList<E> extends AbstractSequentialList<E> implements List&

java 集合(LinkedList)

LinkedList特有方法: 1.方法介绍(看代码) import java.util.*; import java.util.ListIterator; /** * Created by lenovo on 2016/8/21. */ public class ex12 { public static void main(String[] args) { LinkedList list = new LinkedList(); list.add("小明"); list.add(&qu

Java 集合之LinkedList源码分析

1.介绍 链表是数据结构中一种很重要的数据结构,一个链表含有一个或者多个节点,每个节点处理保存自己的信息之外还需要保存上一个节点以及下一个节点的指针信息.通过链表的表头就可以访问整个链表的信息.Java API中提供了链表的Java实现---LinkedList下.LinkedList是通过节点的连接实现链表的数据结构,向linkedList中插入或删除元素的速度是特别快,而随机访问的速度相对较慢,这个是由于链表本身的性质造成的,在链表中,每个节点都包含了前一个节点的引用,后一个节点的引用和节点

java集合框架--ArrayList类、Vector和LinkedList类

1.ArrayList类概述 底层数据结构是数组,查询块,增删慢. 线程不安全,效率高. 2.ArrayList案例 2.1存储字符串并遍历 package com; import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; public class ArrayListDemo { public static void main(String[] args) { //创建ArrayL

Java集合源码学习笔记(三)LinkedList分析

前面学习了ArrayList的源码,数组是顺序存储结构,存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二分查找时间复杂度小,为O(1),数组的特点是寻址容易,插入和删除困难.今天学习另外的一种常用数据结构LinkedList的实现,LinkedList使用链表作为存储结构,链表是线性存储结构,在内存上不是连续的一段空间,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N),链表的特点是寻址困难,插入和删除容易.所有的代码都基于JDK 1.6. >>关于LinkedLis

Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对LinkedList有个整体认识,然后再学习它的源码:最后再通过实例来学会使用LinkedList.内容包括:第1部分 LinkedList介绍第2部分 LinkedList数据结构第3部分 LinkedList源码解析(基于JDK1.6.0_45)第4部分 LinkedList遍历方式第5部分 LinkedL

Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例

java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例 概要  和学习ArrayList一样,接下来呢,我们先对LinkedList有个整体认识,然后再学习它的源码:最后再通过实例来学会使用LinkedList.内容包括:第1部分 LinkedList介绍第2部分 LinkedList数