(二十七) LinkedList

前言:之前写了HashMap 和 ArrayList,还差 LinkedList 和 HashTable 没有学习,这篇学习下LinkedList。

PS: 源码分析针对于Android O 的jdk,即jdk1.8

同事推荐的流程图画图网站:https://www.processon.com/


demo地址:jiatai的demo


1. LinkedList简单介绍

LinkedList从实现方面用的数据结构是双向链表,它也可以被当作堆栈、队列或双端队列进行操作,不需要考虑扩容。


2. LinkedList简单使用

简单的往LinkedList里面塞数字0-9:

package com.example.demo_27_linkedlist;

import java.util.LinkedList;

public class MyClass {
    public static void main(String[] args){
        LinkedList linkedList = new LinkedList<Integer>();
        linkedList.add(0);
        for (int i = 1; i < 10; i++) {
            linkedList.add(i);
        }
        System.out.print(linkedList);
    }
}

对应log打印:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


3. 简单使用所对应的内在

上面的使用主要涉及了三个方法

  • 构造方法
  • add()
  • toString()

下面来简单地看下内在=-=

3.1 无参构造方法

很光棍,没啥看的。

    /**
     * Constructs an empty list.
     */
    public LinkedList() {
    }


3.2 add(E e)

 /**
     * Appends the specified element to the end of this list.
     *
     * <p>This method is equivalent to {@link #addLast}.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
 /**
     * Links e as last element.
     */
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

从上面的代码可以看出,使用add(E e)往LinkedList里添加元素的时候,会缓存一个last成员变量,实时记录上一次添加的元素,即链表的尾巴,添加完成后,更新链表的尾巴为新添加的元素。当链表为空的时候,last自然是null,所以第一个添加的元素即为first也为last。

这里再关注一下Node的构造方法,传入了last/E/null,分别对应prev、element和next。

来看下源码,一个简单的双向链表静态内部类:

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的每个元素都具有索引它前后元素的能力,即linkedList是个双向链表。


3.3 toString()

和ArrayList 一样的,使用了继承自父类AbstractCollection的toString方法。

/**
     * Returns a string representation of this collection.  The string
     * representation consists of a list of the collection's elements in the
     * order they are returned by its iterator, enclosed in square brackets
     * (<tt>"[]"</tt>).  Adjacent elements are separated by the characters
     * <tt>", "</tt> (comma and space).  Elements are converted to strings as
     * by {@link String#valueOf(Object)}.
     *
     * @return a string representation of this collection
     */
    public String toString() {
        Iterator<E> it = iterator();
        if (! it.hasNext())
            return "[]";

        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }

这里贴一下自己画的继承关系图


3.4 get(int dex)

// Positional Access Operations

    /**
     * Returns the element at the specified position in this list.
     *
     * @param index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    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的get方法相比ArrayList效率肯定是低不少的,从上面可以看出linkedList是做了优化的,如果是前一半就从first开始遍历,如果是后一半就从last开始往前遍历,而ArrayList不需要这么遍历,直接就可以获取对应index的元素了。


3.5 add(int index, E element)

 /**
     * Inserts the specified element at the specified position in this list.
     * Shifts the element currently at that position (if any) and any
     * subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
/**
     * Inserts element e before non-null Node succ.
     */
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = 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++;
    }

add的过程其实包含了get和重新链接的过程,先找到对应index的链表元素,然后将新插入元素放在该元素前面,重新处理前后的链接关系。比ArrayList的劣势在于get的劣势,而插入就很容易了,第一不要考虑扩容,第二不需要挪一下后面所有元素。在元素很多的情况下显然LinkedList效率会更高。


4. 总结

就如我们一直所知LinkedList是个双向链表,与ArrayList相比,获取指定位置的元素效率较低,但是插入和移除元素的效率是更高的。

猜你喜欢

转载自blog.csdn.net/sinat_20059415/article/details/80026446