Java常用集合LinkedList分析

LinkedList底層數(shù)據(jù)結(jié)構(gòu)是一個(gè)具有頭尾節(jié)點(diǎn)的雙向鏈表。雙向鏈表的優(yōu)點(diǎn)就是通過(guò)某個(gè)節(jié)點(diǎn)訪問(wèn)其前驅(qū)和后驅(qū)表較方便。缺點(diǎn)是需要額外存儲(chǔ)其前驅(qū)和后驅(qū)的空間。LinkeList鏈表允許null元素;
問(wèn)題一:LinkedList如何初始化

LinkedList構(gòu)造函數(shù)有兩種,無(wú)參構(gòu)造和帶集合的構(gòu)造函數(shù)
1.無(wú)參構(gòu)造,初始化集合大小size=0,初始不用存儲(chǔ)任何數(shù)據(jù),內(nèi)存占用低;
 public LinkedList() {
        this.size = 0;
 }

2.帶集合的構(gòu)造函數(shù),創(chuàng)建一個(gè)大小和傳入集合大小一致的鏈表,記錄頭尾節(jié)點(diǎn)
 public LinkedList(Collection<? extends E> c) {
        this();
        this.addAll(c);
 }
3.接著2我們來(lái)看下addAll方法,他的參數(shù)有index和傳入的集合,猜想index應(yīng)該是傳入集合存儲(chǔ)開始的地方。
 public boolean addAll(int index, Collection<? extends E> c) {
        //判斷這個(gè)index是否超過(guò)鏈表有效位置的范圍,超出的話會(huì)報(bào)IndexOutOfBoundsException.
        this.checkPositionIndex(index);
       //返回包含這個(gè)集合中所有數(shù)據(jù)的數(shù)組,感覺(jué)是個(gè)耗時(shí)操作
        Object[] a = c.toArray();
      //獲取參數(shù)集合的長(zhǎng)度,是構(gòu)造之后鏈表的長(zhǎng)度
        int numNew = a.length;
     //參數(shù)集合長(zhǎng)度為0,說(shuō)明不需要存儲(chǔ)任何數(shù)據(jù),就不用做任何事了直接返回。
        if (numNew == 0) {
            return false;
        } else {
      //輔助構(gòu)造鏈表
            LinkedList.Node pred;
            LinkedList.Node succ;
        //index = this.size從鏈表尾部插入
            if (index == this.size) {
                succ = null;
                pred = this.last;
            } else {
       //index != this.size從鏈表中間插入
                succ = this.node(index);
                pred = succ.prev;
            }

            Object[] var7 = a;
            int var8 = a.length;
        //遍歷參數(shù)集合的數(shù)組,從插入的位置index開始插入
            for(int var9 = 0; var9 < var8; ++var9) {
                Object o = var7[var9];
                LinkedList.Node<E> newNode = new LinkedList.Node(pred, o, (LinkedList.Node)null);
              //插入集合的第一個(gè)節(jié)點(diǎn)
                if (pred == null) {
                    this.first = newNode;
                } else {
               //上一個(gè)節(jié)點(diǎn)記錄一下當(dāng)前節(jié)點(diǎn),形成鏈表
                    pred.next = newNode;
                }
                //pred記錄當(dāng)前節(jié)點(diǎn),開始下一個(gè)節(jié)點(diǎn)的插入
                pred = newNode;
            }
           //succ =null說(shuō)明是從鏈表尾部開始插入的,所以記錄一下鏈表頭部,就行了。
            if (succ == null) {
                this.last = pred;
            } else {
           //相反的不是從鏈表尾部插入,從第index個(gè)插入,index元素排在插入的數(shù)據(jù)后面,形成新鏈表。
                pred.next = succ;
                succ.prev = pred;
            }
         //記錄新的鏈表長(zhǎng)度
            this.size += numNew;
            ++this.modCount;
            return true;
        }
    }

問(wèn)題二:LinkedList怎么插入數(shù)據(jù)?
LinkedList堆插入操作做了相關(guān)優(yōu)化。由于其基于雙端鏈表實(shí)現(xiàn),所以其在頭尾插入速度較快,在鏈表中部插入相對(duì)較慢。

    1.頭部插入元素,直接通過(guò)頭節(jié)點(diǎn)first插入,頭節(jié)點(diǎn)變成第二個(gè)元素,更新頭節(jié)點(diǎn),非常簡(jiǎn)單。
    private void linkFirst(E e) {
        LinkedList.Node<E> f = this.first;
        LinkedList.Node<E> newNode = new LinkedList.Node((LinkedList.Node)null, e, f);
        this.first = newNode;
        if (f == null) {
            this.last = newNode;
        } else {
            f.prev = newNode;
        }

        ++this.size;
        ++this.modCount;
    }
 2.尾部插入元素,直接通過(guò)尾節(jié)點(diǎn)插入新元素 ,待插入節(jié)點(diǎn)變成新的尾節(jié)點(diǎn)。
    void linkLast(E e) {
        LinkedList.Node<E> l = this.last;
        LinkedList.Node<E> newNode = new LinkedList.Node(l, e, (LinkedList.Node)null);
        this.last = newNode;
        if (l == null) {
            this.first = newNode;
        } else {
            l.next = newNode;
        }

        ++this.size;
        ++this.modCount;
    }
 3.中間插入元素,如果index=鏈表長(zhǎng)度則表示在鏈表尾部插入數(shù)據(jù),否則實(shí)在指定元素之前插入元素。
    public void add(int index, E element) {
        this.checkPositionIndex(index);
        if (index == this.size) {
            this.linkLast(element);
        } else {
            this.linkBefore(element, this.node(index));
        }
    }
 4.在看linkBefore之前我看下node方法,這個(gè)方法是通過(guò)index找到鏈表中的某個(gè)數(shù)據(jù)。通過(guò)以下代碼我們可以看到它使用了一次二分查找,提高了一次查找效率,但是隨著數(shù)據(jù)增多這個(gè)函數(shù)的效率會(huì)變得越來(lái)越低。
   LinkedList.Node<E> node(int index) {
        LinkedList.Node x;
        int i;
        if (index < this.size >> 1) {
            x = this.first;
            for(i = 0; i < index; ++i) {
                x = x.next;
            }
            return x;
        } else {
            x = this.last;
            for(i = this.size - 1; i > index; --i) {
                x = x.prev;
            }
            return x;
        }
    }
 5.接著看下linkBefore方法。找到指定元素就非常好辦了,生成新節(jié)點(diǎn),把新節(jié)點(diǎn)放在指定元素的前面就可以了。
    void linkBefore(E e, LinkedList.Node<E> succ) {
        LinkedList.Node<E> pred = succ.prev;
        LinkedList.Node<E> newNode = new LinkedList.Node(pred, e, succ);
        succ.prev = newNode;
     //如果succ是頭節(jié)點(diǎn),還需要更新頭節(jié)點(diǎn)指針。
        if (pred == null) {
            this.first = newNode;
        } else {
            pred.next = newNode;
        }
        ++this.size;
        ++this.modCount;
    }

問(wèn)題三:LinkedList怎么刪除元素
刪除節(jié)點(diǎn)和增加節(jié)點(diǎn)都是通過(guò)node方法找到指定元素,然后刪除指定節(jié)點(diǎn),鏈表的基本操作,這里不再贅敘。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容