国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

集合框架源碼學(xué)習(xí)之LinkedList

kumfo / 1597人閱讀

摘要:它們會在鏈表為空時,拋出獲取尾節(jié)點數(shù)據(jù)方法兩者區(qū)別方法在鏈表為空時,會拋出,而則不會,只是會返回。

目錄:

0-1. 簡介

0-2. 內(nèi)部結(jié)構(gòu)分析

0-3. LinkedList源碼分析

  0-3-1. 構(gòu)造方法

  0-3-2. 添加add方法
  

  0-3-3. 根據(jù)位置取數(shù)據(jù)的方法

  0-3-4. 根據(jù)對象得到索引的方法

  0-3-5. 檢查鏈表是否包含某對象的方法

  0-3-6. 刪除removepop方法

0-4. LinkedList類常用方法

簡介

LinkedList是一個實現(xiàn)了List接口Deque接口雙端鏈表
LinkedList底層的鏈表結(jié)構(gòu)使它支持高效的插入和刪除操作,另外它實現(xiàn)了Deque接口,使得LinkedList類也具有隊列的特性;
LinkedList不是線程安全的,如果想使LinkedList變成線程安全的,可以調(diào)用靜態(tài)類Collections類中的synchronizedList方法:

List list=Collections.synchronizedList(new LinkedList(...));
內(nèi)部結(jié)構(gòu)分析

如下圖所示:

看完了圖之后,我們再看LinkedList類中的一個內(nèi)部私有類Node就很好理解了:

private static class Node {
        E item;//節(jié)點值
        Node next;//前驅(qū)節(jié)點
        Node prev;//后繼節(jié)點

        Node(Node prev, E element, Node next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

這個類就代表雙端鏈表的節(jié)點Node。這個類有三個屬性,分別是前驅(qū)節(jié)點,本節(jié)點的值,后繼結(jié)點。

LinkedList源碼分析 構(gòu)造方法

空構(gòu)造方法:

    public LinkedList() {
    }

用已有的集合創(chuàng)建鏈表的構(gòu)造方法:

    public LinkedList(Collection c) {
        this();
        addAll(c);
    }
添加(add)方法

add(E e) 方法:將元素添加到鏈表尾部

public boolean add(E e) {
        linkLast(e);//這里就只調(diào)用了這一個,我們馬上就分析這個方法的實現(xiàn)
        return true;
    }
private void linkFirst(E e) {
        final Node f = first;
        final Node newNode = new Node<>(null, e, f);//新建節(jié)點,以頭節(jié)點為后繼節(jié)點
        first = newNode;
        //如果鏈表為空,last節(jié)點也指向該節(jié)點
        if (f == null)
            last = newNode;
        //否則,將頭節(jié)點的前驅(qū)指針指向新節(jié)點
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

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

linkBefore方法需要給定兩個參數(shù),一個插入節(jié)點的值,一個指定的node,所以我們又調(diào)用了Node(index)去找到index對應(yīng)的node

addAll(Collection c ): 將集合插入到鏈表尾部

public boolean addAll(Collection c) {
        return addAll(size, c);
    }

addAll(int index, Collection c): 將集合從指定位置開始插入

public boolean addAll(int index, Collection c) {
        //1:檢查index范圍是否在size之內(nèi)
        checkPositionIndex(index);

        //2:toArray()方法把集合的數(shù)據(jù)存到對象數(shù)組中
        Object[] a = c.toArray();
        int numNew = a.length;
        if (numNew == 0)
            return false;

        //3:得到插入位置的前驅(qū)節(jié)點和后繼節(jié)點
        Node pred, succ;
        //如果插入位置為尾部,前驅(qū)節(jié)點為last,后繼節(jié)點為null
        if (index == size) {
            succ = null;
            pred = last;
        }
        //否則,調(diào)用node()方法得到后繼節(jié)點,再得到前驅(qū)節(jié)點
        else {
            succ = node(index);
            pred = succ.prev;
        }

        // 4:遍歷數(shù)據(jù)將數(shù)據(jù)插入
        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            //創(chuàng)建新節(jié)點
            Node newNode = new Node<>(pred, e, null);
            //如果插入位置在鏈表頭部
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }

        //如果插入位置在尾部,重置last節(jié)點
        if (succ == null) {
            last = pred;
        }
        //否則,將插入的鏈表與先前鏈表連接起來
        else {
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew;
        modCount++;
        return true;
    }    

上面可以看出addAll方法通常包括下面四個步驟:

檢查index范圍是否在size之內(nèi)

toArray()方法把集合的數(shù)據(jù)存到對象數(shù)組中

得到插入位置的前驅(qū)和后繼節(jié)點

遍歷數(shù)據(jù),將數(shù)據(jù)插入到指定位置

addFirst(E e): 將元素添加到鏈表頭部

 public void addFirst(E e) {
        linkFirst(e);
    }
private void linkFirst(E e) {
        final Node f = first;
        final Node newNode = new Node<>(null, e, f);//新建節(jié)點,以頭節(jié)點為后繼節(jié)點
        first = newNode;
        //如果鏈表為空,last節(jié)點也指向該節(jié)點
        if (f == null)
            last = newNode;
        //否則,將頭節(jié)點的前驅(qū)指針指向新節(jié)點
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

addLast(E e): 將元素添加到鏈表尾部,與 add(E e) 方法一樣

public void addLast(E e) {
        linkLast(e);
    }
根據(jù)位置取數(shù)據(jù)的方法

get(int index)::根據(jù)指定索引返回數(shù)據(jù)

public E get(int index) {
        //檢查index范圍是否在size之內(nèi)
        checkElementIndex(index);
        //調(diào)用Node(index)去找到index對應(yīng)的node然后返回它的值
        return node(index).item;
    }

獲取頭節(jié)點(index=0)數(shù)據(jù)方法:

public E getFirst() {
        final Node f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }
public E element() {
        return getFirst();
    }
public E peek() {
        final Node f = first;
        return (f == null) ? null : f.item;
    }

public E peekFirst() {
        final Node f = first;
        return (f == null) ? null : f.item;
     }

區(qū)別:
getFirst(),element(),peek(),peekFirst()
這四個獲取頭結(jié)點方法的區(qū)別在于對鏈表為空時的處理,是拋出異常還是返回null,其中getFirst()element() 方法將會在鏈表為空時,拋出異常

element()方法的內(nèi)部就是使用getFirst()實現(xiàn)的。它們會在鏈表為空時,拋出NoSuchElementException
獲取尾節(jié)點(index=-1)數(shù)據(jù)方法:

 public E getLast() {
        final Node l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }
 public E peekLast() {
        final Node l = last;
        return (l == null) ? null : l.item;
    }

兩者區(qū)別:
getLast() 方法在鏈表為空時,會拋出NoSuchElementException,而peekLast() 則不會,只是會返回 null

根據(jù)對象得到索引的方法

int indexOf(Object o): 從頭遍歷找

public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            //從頭遍歷
            for (Node x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            //從頭遍歷
            for (Node x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

int lastIndexOf(Object o): 從尾遍歷找

public int lastIndexOf(Object o) {
        int index = size;
        if (o == null) {
            //從尾遍歷
            for (Node x = last; x != null; x = x.prev) {
                index--;
                if (x.item == null)
                    return index;
            }
        } else {
            //從尾遍歷
            for (Node x = last; x != null; x = x.prev) {
                index--;
                if (o.equals(x.item))
                    return index;
            }
        }
        return -1;
    }
檢查鏈表是否包含某對象的方法:

contains(Object o): 檢查對象o是否存在于鏈表中

 public boolean contains(Object o) {
        return indexOf(o) != -1;
    }
刪除(remove/pop)方法

remove() ,removeFirst(),pop(): 刪除頭節(jié)點

public E pop() {
        return removeFirst();
    }
public E remove() {
        return removeFirst();
    }
public E removeFirst() {
        final Node f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }

removeLast(),pollLast(): 刪除尾節(jié)點

public E removeLast() {
        final Node l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }
public E pollLast() {
        final Node l = last;
        return (l == null) ? null : unlinkLast(l);
    }

區(qū)別: removeLast()在鏈表為空時將拋出NoSuchElementException,而pollLast()方法返回null。

remove(Object o): 刪除指定元素

public boolean remove(Object o) {
        //如果刪除對象為null
        if (o == null) {
            //從頭開始遍歷
            for (Node x = first; x != null; x = x.next) {
                //找到元素
                if (x.item == null) {
                   //從鏈表中移除找到的元素
                    unlink(x);
                    return true;
                }
            }
        } else {
            //從頭開始遍歷
            for (Node x = first; x != null; x = x.next) {
                //找到元素
                if (o.equals(x.item)) {
                    //從鏈表中移除找到的元素
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

當刪除指定對象時,只需調(diào)用remove(Object o)即可,不過該方法一次只會刪除一個匹配的對象,如果刪除了匹配對象,返回true,否則false。

unlink(Node x) 方法:

E unlink(Node x) {
        // assert x != null;
        final E element = x.item;
        final Node next = x.next;//得到后繼節(jié)點
        final Node prev = x.prev;//得到前驅(qū)節(jié)點

        //刪除前驅(qū)指針
        if (prev == null) {
            first = next;如果刪除的節(jié)點是頭節(jié)點,令頭節(jié)點指向該節(jié)點的后繼節(jié)點
        } else {
            prev.next = next;//將前驅(qū)節(jié)點的后繼節(jié)點指向后繼節(jié)點
            x.prev = null;
        }

        //刪除后繼指針
        if (next == null) {
            last = prev;//如果刪除的節(jié)點是尾節(jié)點,令尾節(jié)點指向該節(jié)點的前驅(qū)節(jié)點
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

remove(int index):刪除指定位置的元素

public E remove(int index) {
        //檢查index范圍
        checkElementIndex(index);
        //將節(jié)點刪除
        return unlink(node(index));
    }
LinkedList類常用方法測試
package list;

import java.util.Iterator;
import java.util.LinkedList;

public class LinkedListDemo {
    public static void main(String[] srgs) {
        //創(chuàng)建存放int類型的linkedList
        LinkedList linkedList = new LinkedList<>();
        /************************** linkedList的基本操作 ************************/
        linkedList.addFirst(0); // 添加元素到列表開頭
        linkedList.add(1); // 在列表結(jié)尾添加元素
        linkedList.add(2, 2); // 在指定位置添加元素
        linkedList.addLast(3); // 添加元素到列表結(jié)尾
        
        System.out.println("LinkedList(直接輸出的): " + linkedList);

        System.out.println("getFirst()獲得第一個元素: " + linkedList.getFirst()); // 返回此列表的第一個元素
        System.out.println("getLast()獲得第最后一個元素: " + linkedList.getLast()); // 返回此列表的最后一個元素
        System.out.println("removeFirst()刪除第一個元素并返回: " + linkedList.removeFirst()); // 移除并返回此列表的第一個元素
        System.out.println("removeLast()刪除最后一個元素并返回: " + linkedList.removeLast()); // 移除并返回此列表的最后一個元素
        System.out.println("After remove:" + linkedList);
        System.out.println("contains()方法判斷列表是否包含1這個元素:" + linkedList.contains(1)); // 判斷此列表包含指定元素,如果是,則返回true
        System.out.println("該linkedList的大小 : " + linkedList.size()); // 返回此列表的元素個數(shù)

        /************************** 位置訪問操作 ************************/
        System.out.println("-----------------------------------------");
        linkedList.set(1, 3); // 將此列表中指定位置的元素替換為指定的元素
        System.out.println("After set(1, 3):" + linkedList);
        System.out.println("get(1)獲得指定位置(這里為1)的元素: " + linkedList.get(1)); // 返回此列表中指定位置處的元素

        /************************** Search操作 ************************/
        System.out.println("-----------------------------------------");
        linkedList.add(3);
        System.out.println("indexOf(3): " + linkedList.indexOf(3)); // 返回此列表中首次出現(xiàn)的指定元素的索引
        System.out.println("lastIndexOf(3): " + linkedList.lastIndexOf(3));// 返回此列表中最后出現(xiàn)的指定元素的索引

        /************************** Queue操作 ************************/
        System.out.println("-----------------------------------------");
        System.out.println("peek(): " + linkedList.peek()); // 獲取但不移除此列表的頭
        System.out.println("element(): " + linkedList.element()); // 獲取但不移除此列表的頭
        linkedList.poll(); // 獲取并移除此列表的頭
        System.out.println("After poll():" + linkedList);
        linkedList.remove();
        System.out.println("After remove():" + linkedList); // 獲取并移除此列表的頭
        linkedList.offer(4);
        System.out.println("After offer(4):" + linkedList); // 將指定元素添加到此列表的末尾

        /************************** Deque操作 ************************/
        System.out.println("-----------------------------------------");
        linkedList.offerFirst(2); // 在此列表的開頭插入指定的元素
        System.out.println("After offerFirst(2):" + linkedList);
        linkedList.offerLast(5); // 在此列表末尾插入指定的元素
        System.out.println("After offerLast(5):" + linkedList);
        System.out.println("peekFirst(): " + linkedList.peekFirst()); // 獲取但不移除此列表的第一個元素
        System.out.println("peekLast(): " + linkedList.peekLast()); // 獲取但不移除此列表的第一個元素
        linkedList.pollFirst(); // 獲取并移除此列表的第一個元素
        System.out.println("After pollFirst():" + linkedList);
        linkedList.pollLast(); // 獲取并移除此列表的最后一個元素
        System.out.println("After pollLast():" + linkedList);
        linkedList.push(2); // 將元素推入此列表所表示的堆棧(插入到列表的頭)
        System.out.println("After push(2):" + linkedList);
        linkedList.pop(); // 從此列表所表示的堆棧處彈出一個元素(獲取并移除列表第一個元素)
        System.out.println("After pop():" + linkedList);
        linkedList.add(3);
        linkedList.removeFirstOccurrence(3); // 從此列表中移除第一次出現(xiàn)的指定元素(從頭部到尾部遍歷列表)
        System.out.println("After removeFirstOccurrence(3):" + linkedList);
        linkedList.removeLastOccurrence(3); // 從此列表中移除最后一次出現(xiàn)的指定元素(從頭部到尾部遍歷列表)
        System.out.println("After removeFirstOccurrence(3):" + linkedList);

        /************************** 遍歷操作 ************************/
        System.out.println("-----------------------------------------");
        linkedList.clear();
        for (int i = 0; i < 100000; i++) {
            linkedList.add(i);
        }
        // 迭代器遍歷
        long start = System.currentTimeMillis();
        Iterator iterator = linkedList.iterator();
        while (iterator.hasNext()) {
            iterator.next();
        }
        long end = System.currentTimeMillis();
        System.out.println("Iterator:" + (end - start) + " ms");

        // 順序遍歷(隨機遍歷)
        start = System.currentTimeMillis();
        for (int i = 0; i < linkedList.size(); i++) {
            linkedList.get(i);
        }
        end = System.currentTimeMillis();
        System.out.println("for:" + (end - start) + " ms");

        // 另一種for循環(huán)遍歷
        start = System.currentTimeMillis();
        for (Integer i : linkedList)
            ;
        end = System.currentTimeMillis();
        System.out.println("for2:" + (end - start) + " ms");

        // 通過pollFirst()或pollLast()來遍歷LinkedList
        LinkedList temp1 = new LinkedList<>();
        temp1.addAll(linkedList);
        start = System.currentTimeMillis();
        while (temp1.size() != 0) {
            temp1.pollFirst();
        }
        end = System.currentTimeMillis();
        System.out.println("pollFirst()或pollLast():" + (end - start) + " ms");

        // 通過removeFirst()或removeLast()來遍歷LinkedList
        LinkedList temp2 = new LinkedList<>();
        temp2.addAll(linkedList);
        start = System.currentTimeMillis();
        while (temp2.size() != 0) {
            temp2.removeFirst();
        }
        end = System.currentTimeMillis();
        System.out.println("removeFirst()或removeLast():" + (end - start) + " ms");
    }
}

歡迎關(guān)注我的微信公眾號(分享各種Java學(xué)習(xí)資源,面試題,以及企業(yè)級Java實戰(zhàn)項目回復(fù)關(guān)鍵字免費領(lǐng)取):

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/68954.html

相關(guān)文章

  • 集合框架源碼學(xué)習(xí)之HashMap(JDK1.8)

    摘要:所謂拉鏈法就是將鏈表和數(shù)組相結(jié)合。若遇到哈希沖突,則將沖突的值加到鏈表中即可。在編寫程序中,要盡量避免。 目錄: 0-1. 簡介 0-2. 內(nèi)部結(jié)構(gòu)分析   0-2-1. JDK18之前   0-2-2. JDK18之后 0-3. LinkedList源碼分析   0-3-1. 構(gòu)造方法   0-3-2. put方法   0-3-3. get方法   0-3-4. resize方法 ...

    yangrd 評論0 收藏0
  • 一文掌握關(guān)于Java數(shù)據(jù)結(jié)構(gòu)所有知識點(歡迎一起完善)

    摘要:是棧,它繼承于。滿二叉樹除了葉結(jié)點外每一個結(jié)點都有左右子葉且葉子結(jié)點都處在最底層的二叉樹。沒有鍵值相等的節(jié)點。這是數(shù)據(jù)庫選用樹的最主要原因。 在我們學(xué)習(xí)Java的時候,很多人會面臨我不知道繼續(xù)學(xué)什么或者面試會問什么的尷尬情況(我本人之前就很迷茫)。所以,我決定通過這個開源平臺來幫助一些有需要的人,通過下面的內(nèi)容,你會掌握系統(tǒng)的Java學(xué)習(xí)以及面試的相關(guān)知識。本來是想通過Gitbook的...

    keithxiaoy 評論0 收藏0
  • 集合框架源碼學(xué)習(xí)之ArrayList

    摘要:用戶自己指定容量創(chuàng)建大小的數(shù)組創(chuàng)建空數(shù)組默認構(gòu)造函數(shù),其默認初始容量為構(gòu)造一個包含指定集合的元素的列表,按照它們由集合的迭代器返回的順序。以正確的順序返回該列表中的元素的迭代器。此方法充當基于陣列和基于集合的之間的橋梁。 目錄: 0-0-1. 前言 0-0-2. 集合框架知識回顧 0-0-3. ArrayList簡介 0-0-4. ArrayList核心源碼 0-0-5. Ar...

    BLUE 評論0 收藏0
  • Java 容器學(xué)習(xí)之 HashMap

    摘要:底層的數(shù)據(jù)結(jié)構(gòu)就是數(shù)組鏈表紅黑樹,紅黑樹是在中加進來的。負載因子哈希表中的填滿程度。 前言 把 Java 容器的學(xué)習(xí)筆記放到 github 里了,還在更新~其他的目前不打算抽出來作為文章寫,感覺挖的還不夠深,等對某些東西理解的更深了再寫文章吧Java 容器目錄如下: Java 容器 一、概述 二、源碼學(xué)習(xí) 1. Map 1.1 HashMap 1.2 LinkedHashM...

    Alex 評論0 收藏0
  • 前端學(xué)習(xí)之JS框架的使用

    摘要:目前,有三個明確的框架可供選擇。和在眾多開源框架中贏得了開發(fā)人員和公司的信任。雖然這三個框架有許多共同之處,但它們的受歡迎程度因行業(yè)而異。使用,這有助于在編碼時發(fā)現(xiàn)并糾正常見錯誤。 人們首先注意到的是你的應(yīng)用程序的視覺吸引力。大多數(shù)用戶傾向于將界面設(shè)計與公司的信譽和專業(yè)能力聯(lián)系起來。這就是為什么選擇正確的前端技術(shù)對你的業(yè)務(wù)...

    不知名網(wǎng)友 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<