Java基礎【九】 - 集合 java.uti.LinkedList

依賴關係

LinkedList 繼承於AbstractSequentialList的雙向鏈表。它也能夠被看成堆棧、隊列或雙端隊列進行操做。
LinkedList 實現 List 接口,能對它進行隊列操做。
LinkedList 實現 Deque 接口,即能將LinkedList看成雙端隊列使用。
LinkedList 實現了Cloneable接口,即覆蓋了函數clone(),能克隆。
LinkedList 實現java.io.Serializable接口,這意味着LinkedList支持序列化,能經過序列化去傳輸。
LinkedList 是非同步的。html

依賴關係圖

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

API

LinkedList apijava

屬性

// 實際元素個數
transient int size = 0;
// 頭節點
transient Node<E> first;
// 尾節點
transient Node<E> last;
//注意,頭節點、尾節點都有transient關鍵字修飾,這也意味着在序列化時該域是不會序列化的。

內部類

內部類Node就是實際的節點,用於存放實際元素的地方node

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

add(E e)添加元素到尾部

public boolean add(E e) {
    linkLast(e);
    return true;
}

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)添加元素到尾部圖解

圖片描述

add(int index,E e)添加元素到指定位置

public void add(int index, E element) {
    checkPositionIndex(index); //檢查索引是否處於[0-size]之間

    if (index == size)//添加在鏈表尾部
        linkLast(element);
    else//添加在鏈表中間
        linkBefore(element, node(index));
}

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(int index,E e)添加元素到指定位置圖解

圖片描述

addAll添加集合到指定位置圖解

圖片描述

刪除和添加相反不作多餘解釋了。api

總結

一、LinkedList 其實是經過雙向鏈表去實現的,增刪效率高,索引效率低。它包含一個很是重要的內部類:Node。Node是雙向鏈表節點所對應的數據結構,它包括的屬性有:當前節點所包含的值,上一個節點,下一個節點。
二、從LinkedList的實現方式中能夠發現,它不存在LinkedList容量不足的問題。
三、LinkedList實現java.io.Serializable。當寫入到輸出流時,先寫入「容量」,再依次寫入「每個節點保護的值」;當讀出輸入流時,先讀取「容量」,再依次讀取「每個元素」。
四、因爲LinkedList實現了Deque,而Deque接口定義了在雙端隊列兩端訪問元素的方法。提供插入、移除和檢查元素的方法。每種方法都存在兩種形式:一種形式在操做失敗時拋出異常,另外一種形式返回一個特殊值(null 或 false,具體取決於操做)。數據結構

遍歷效率對比實例

import java.util.List;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;

/*
 * @desc 測試LinkedList的幾種遍歷方式和效率
 */
public class LinkedListThruTest {
    public static void main(String[] args) {
        // 經過Iterator遍歷LinkedList
        iteratorLinkedListThruIterator(getLinkedList()) ;
        
        // 經過快速隨機訪問遍歷LinkedList
        iteratorLinkedListThruForeach(getLinkedList()) ;

        // 經過for循環的變種來訪問遍歷LinkedList
        iteratorThroughFor2(getLinkedList()) ;

        // 經過PollFirst()遍歷LinkedList
        iteratorThroughPollFirst(getLinkedList()) ;

        // 經過PollLast()遍歷LinkedList
        iteratorThroughPollLast(getLinkedList()) ;

        // 經過removeFirst()遍歷LinkedList
        iteratorThroughRemoveFirst(getLinkedList()) ;

        // 經過removeLast()遍歷LinkedList
        iteratorThroughRemoveLast(getLinkedList()) ;
    }
    
    private static LinkedList getLinkedList() {
        LinkedList llist = new LinkedList();
        for (int i=0; i<100000; i++)
            llist.addLast(i);

        return llist;
    }
    /**
     * 經過快迭代器遍歷LinkedList
     */
    private static void iteratorLinkedListThruIterator(LinkedList<Integer> list) {
        if (list == null)
            return ;

        // 記錄開始時間
        long start = System.currentTimeMillis();
        
        for(Iterator iter = list.iterator(); iter.hasNext();)
            iter.next();

        // 記錄結束時間
        long end = System.currentTimeMillis();
        long interval = end - start;
        System.out.println("iteratorLinkedListThruIterator:" + interval+" ms");
    }

    /**
     * 經過快速隨機訪問遍歷LinkedList
     */
    private static void iteratorLinkedListThruForeach(LinkedList<Integer> list) {
        if (list == null)
            return ;

        // 記錄開始時間
        long start = System.currentTimeMillis();
        
        int size = list.size();
        for (int i=0; i<size; i++) {
            list.get(i);        
        }
        // 記錄結束時間
        long end = System.currentTimeMillis();
        long interval = end - start;
        System.out.println("iteratorLinkedListThruForeach:" + interval+" ms");
    }

    /**
     * 經過另一種for循環來遍歷LinkedList
     */
    private static void iteratorThroughFor2(LinkedList<Integer> list) {
        if (list == null)
            return ;

        // 記錄開始時間
        long start = System.currentTimeMillis();
        
        for (Integer integ:list) 
            ;

        // 記錄結束時間
        long end = System.currentTimeMillis();
        long interval = end - start;
        System.out.println("iteratorThroughFor2:" + interval+" ms");
    }

    /**
     * 經過pollFirst()來遍歷LinkedList
     */
    private static void iteratorThroughPollFirst(LinkedList<Integer> list) {
        if (list == null)
            return ;

        // 記錄開始時間
        long start = System.currentTimeMillis();
        while(list.pollFirst() != null)
            ;

        // 記錄結束時間
        long end = System.currentTimeMillis();
        long interval = end - start;
        System.out.println("iteratorThroughPollFirst:" + interval+" ms");
    }

    /**
     * 經過pollLast()來遍歷LinkedList
     */
    private static void iteratorThroughPollLast(LinkedList<Integer> list) {
        if (list == null)
            return ;

        // 記錄開始時間
        long start = System.currentTimeMillis();
        while(list.pollLast() != null)
            ;

        // 記錄結束時間
        long end = System.currentTimeMillis();
        long interval = end - start;
        System.out.println("iteratorThroughPollLast:" + interval+" ms");
    }

    /**
     * 經過removeFirst()來遍歷LinkedList
     */
    private static void iteratorThroughRemoveFirst(LinkedList<Integer> list) {
        if (list == null)
            return ;

        // 記錄開始時間
        long start = System.currentTimeMillis();
        try {
            while(list.removeFirst() != null)
                ;
        } catch (NoSuchElementException e) {
        }

        // 記錄結束時間
        long end = System.currentTimeMillis();
        long interval = end - start;
        System.out.println("iteratorThroughRemoveFirst:" + interval+" ms");
    }

    /**
     * 經過removeLast()來遍歷LinkedList
     */
    private static void iteratorThroughRemoveLast(LinkedList<Integer> list) {
        if (list == null)
            return ;

        // 記錄開始時間
        long start = System.currentTimeMillis();
        try {
            while(list.removeLast() != null)
                ;
        } catch (NoSuchElementException e) {
        }

        // 記錄結束時間
        long end = System.currentTimeMillis();
        long interval = end - start;
        System.out.println("iteratorThroughRemoveLast:" + interval+" ms");
    }

}

輸出結果函數

iteratorLinkedListThruIterator:8 ms
iteratorLinkedListThruForeach:3724 ms
iteratorThroughFor2:5 ms
iteratorThroughPollFirst:8 ms
iteratorThroughPollLast:6 ms
iteratorThroughRemoveFirst:2 ms
iteratorThroughRemoveLast:2 ms

因而可知,遍歷LinkedList時,使用removeFist()或removeLast()效率最高。但用它們遍歷時,會刪除原始數據;若單純只讀取,而不刪除,應該使用第3種遍歷方式。
不管如何,千萬不要經過隨機訪問去遍歷LinkedList!測試

相關文章
相關標籤/搜索