• 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
  • 相关阅读:
    什么是Netflix Feign?它的优点是什么?
    Spring Boot 自动配置原理是什么?
    springcloud断路器作用?
    什么是SpringCloudConfig?
    find命令查找包含指定内容的文件
    @PostConstruct使用总结
    @Retention 注解的作用
    SpringBoot自定义Condition注解
    Spring Boot 入门
    SpringBoot +MSSQL
  • 原文地址:https://www.cnblogs.com/chenpi/p/5269737.html
Copyright © 2020-2023  润新知