1. 程式人生 > >JDK1.8集合框架原始碼分析二-------------LinkedList

JDK1.8集合框架原始碼分析二-------------LinkedList

0.LinkedList特點

   0.1) 刪除或新增效率高:不會移動資料元素,只會維護內部節點的關係

   0.2) 查詢慢: 在其內部沒有下標,也即沒有索引,需要使用for迴圈遍歷,LInkedList為了提高效率,採用折半查詢演算法

1.LinkedList的成員屬性

   1.1  int size --- LinkedList容器現有的節點個數

   1.2 Node<E> first;--- 首節點,目的是為了查詢方便,從裡可以發起查詢 

   1.3 Node<E> last--- 尾節點,目的: (1) 從這裡新增元素 (2)也可以從這裡發起查詢

2.LinkedList的Node類的資料結構

    private static class Node<E>{
        E data;//節點資料
        Node<E> prev;//上一個節點
        Node<E> next;//下一個節點

        public Node(E data, Node<E> prev, Node<E> next) {
            this.data = data;
            this.prev = prev;
            this.next = next;
        }
    }

3.LinkedList的資料結構

4.LinkedList新增元素

    private void linkLast(E e) {
        //建立一個臨時變數儲存容器的尾節點
        final Node<E> l = last;
        //建立一個以當前容器尾節點為上一個節點,null為下一個節點的新節點
        final Node<E> newNode = new Node<E>(e, l, null);
        //把新節點賦值給尾節點
        last = newNode;
        //如果首節點為空
        if(first == null){
            first = newNode;
        }else{
            l.next = newNode;
        }
        size ++ ;
    }

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

5.LinkedList 指定下標index查詢元素

    5.1) 陣列越界檢查   index >=0 && index < size

    5.2) 根據當前容器所儲存的資料大小進行折半查詢

          index < size >> 1 ====> index < size/2 從容器的首節點開始查詢

          否則 從容器的尾節點開始查詢                                                                                                                                               

                                 

    @Override
    public E get(int index) {
        checkElementIndex(index);
        return node(index).data;
    }

    private void checkElementIndex(int index) {
        if (index >= 0 && index < size) {
            return;
        }
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    private Node<E> node(int index) {
        //即要想找到下標為index的節點,則只需要找到其相鄰節點即可得到該節點的資料
        Node<E> node = null;
        if(index < size >> 1){
            node = first;
            for (int i = 0; i < index; i ++){
                node = node.next;
            }
        }else{
            node = last;
            for (int i = (size -1); i > index; i --){
                node = node.prev;
            }
        }
        return node;
    }

 6.LinkedList在指定下標index位置新增元素

             6.1) 陣列下標越界檢查   index>=0 && index<=size

             6.2) 找到下標所在的節點indexNode (index >=0 && index < size 只能找到這個範圍的節點) 

             6.3) 下標所在的節點為新節點newNode的下一個節點--->newNode.next = indexNode

             6.4) 下標所在的節點的上一個節點indexOldPrevNode為新節點的上一個節點--->newNode.prev = indexOldPrevNode

             6.4.1) 如果indexNode節點的上一個節點為null,則indexNode當前為first節點,此時需要更新首節點的內容

             6.5)下標所在的節點的上一個節點的新值為新節點--->indexNode.prev = newNode

             6.6 ) 當index = size時 ,直接在容器的末尾新增節點即可

 


    private void linkBefore(E e, Node<E> indexNode) {
        //下標所在的節點的上一個節點
        Node<E> prev = indexNode.prev;
        //利用建構函式建立新節點,
        //新節點的上一個節點為下標所在節點的上一個節點
        //新節點的下一個節點為下標所在的節點
        Node<E> newNode = new Node<>(e,prev,indexNode);
        //下標所在節點的新的上一個節點為新節點
        indexNode.prev = newNode;
        //如果下標所在節點的老的上一個節點為null
        //則說明下標所在節點在插入新的節點之前為首節點
        //現在插入新節點之後,新節點則稱為首節點
        if(prev == null){
            first = newNode;
        }else{
            //如果下標所在節點的老的上一個節點不為null
            //則說明下標所在的節點在插入新的節點之前不是首節點
            //則下標所在的節點的老的上一個節點的下一個節點為新節點
            prev.next = newNode;
        }
        size++;
    }

    @Override
    public boolean add(int index, E e) {
        checkElementIndexInPos(index);

        if (index == size){
            linkLast(e);
        }else{
            linkBefore(e,node(index));
        }
        return true;
    }

7.LinkedList指定下標刪除節點

            7.1) 陣列越界檢查  index >=0 && index < size

            7.2) 找出下標所在的節點 indexNode

            7.3) indexNode.prev = oldIndexPrevNode; indexNode.next = oldIndexNextNode;

            7.4) 如果 oldIndexPrevNode = null,則 first = indexNode ,即刪除的為首節點 此時 需要 first = oldIndexNextNode;

            7.5) 如果 oldIndexPrevNode != null 則 oldIndexPrevNode.next = oldIndexNextNode; 

            7.6) 如果 oldIndexNextNode = null,則 last = indexNode,即刪除的是尾節點,需要last = oldIndexPrevNode;

            7.7) 如果 oldIndexNextNode != null, 則oldIndexNextNode.prev = oldIndexPrevNode;

    private E unlink(Node<E> indexNode) {
        final E data = indexNode.data;
        final Node<E> oldIndexPrevNode = indexNode.prev;
        final Node<E> oldIndexNextNode = indexNode.next;

        if(oldIndexPrevNode == null){
            first = oldIndexNextNode;
        }else{
            oldIndexPrevNode.next = oldIndexNextNode;
            indexNode.prev = null;//GC回收
        }

        if(oldIndexNextNode == null){
            last = oldIndexPrevNode;
        }else{
            oldIndexNextNode.prev = oldIndexPrevNode;
            indexNode.next = null;//GC回收
        }
        indexNode.data = null;//GC回收
        size--;
        return data;
    }

    @Override
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }

 8.LIinkedList指定元素值刪除

         8.1)  找出指定元素所對應的節點

         8.2)  根據節點刪除資料和步驟7一樣

    @Override
    public boolean remove(Object obj) {
        if(obj == null){
            //從首節點開始遍歷,找到複合條件的節點
            for(Node<E> x = first; x.next !=null; x = x.next){
                if(x.data == null){
                    unlink(x);
                    return true;
                }
            }
        }else{
            for(Node<E> x = first; x.next !=null; x = x.next){
                if(obj.equals(x.data)){
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

9.自己手寫LinkedList原始碼程式碼以及Junit測試類

package com.roger.collection.impl;

import com.roger.collection.RogerList;

public class RogerLinkedList<E> implements RogerList<E> {

    //容器中資料的大小
    private int size;
    //容器中首節點
    private Node<E> first;
    //容器中尾節點
    private Node<E> last;

    private void linkLast(E e) {
        //建立一個臨時變數儲存容器的尾節點
        final Node<E> l = last;
        //建立一個以當前容器尾節點為上一個節點,null為下一個節點的新節點
        final Node<E> newNode = new Node<E>(e, l, null);
        //把新節點賦值給尾節點
        last = newNode;
        //如果首節點為空
        if (first == null) {
            first = newNode;
        } else {
            l.next = newNode;
        }
        size++;
    }

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

    private void linkBefore(E e, Node<E> indexNode) {
        //下標所在的節點的上一個節點
        Node<E> prev = indexNode.prev;
        //利用建構函式建立新節點,
        //新節點的上一個節點為下標所在節點的上一個節點
        //新節點的下一個節點為下標所在的節點
        Node<E> newNode = new Node<>(e,prev,indexNode);
        //下標所在節點的新的上一個節點為新節點
        indexNode.prev = newNode;
        //如果下標所在節點的老的上一個節點為null
        //則說明下標所在節點在插入新的節點之前為首節點
        //現在插入新節點之後,新節點則稱為首節點
        if(prev == null){
            first = newNode;
        }else{
            //如果下標所在節點的老的上一個節點不為null
            //則說明下標所在的節點在插入新的節點之前不是首節點
            //則下標所在的節點的老的上一個節點的下一個節點為新節點
            prev.next = newNode;
        }
        size++;
    }

    @Override
    public boolean add(int index, E e) {
        checkElementIndexInPos(index);

        if (index == size){
            linkLast(e);
        }else{
            linkBefore(e,node(index));
        }
        return true;
    }

    private void checkElementIndexInPos(int index) {
        if (index >= 0 && index <= size) {
            return;
        }
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    @Override
    public E get(int index) {
        checkElementIndex(index);
        return node(index).data;
    }

    private void checkElementIndex(int index) {
        if (index >= 0 && index < size) {
            return;
        }
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    private Node<E> node(int index) {
        //即要想找到下標為index的節點,則只需要找到其相鄰節點即可得到該節點的資料
        Node<E> node = null;
        if(index < size >> 1){
            node = first;
            for (int i = 0; i < index; i ++){
                node = node.next;
            }
        }else{
            node = last;
            for (int i = (size -1); i > index; i --){
                node = node.prev;
            }
        }
        return node;
    }

    @Override
    public int size() {
        return size;
    }

    private E unlink(Node<E> indexNode) {
        final E data = indexNode.data;
        final Node<E> oldIndexPrevNode = indexNode.prev;
        final Node<E> oldIndexNextNode = indexNode.next;

        if(oldIndexPrevNode == null){
            first = oldIndexNextNode;
        }else{
            oldIndexPrevNode.next = oldIndexNextNode;
            indexNode.prev = null;//GC回收
        }

        if(oldIndexNextNode == null){
            last = oldIndexPrevNode;
        }else{
            oldIndexNextNode.prev = oldIndexPrevNode;
            indexNode.next = null;//GC回收
        }
        indexNode.data = null;//GC回收
        size--;
        return data;
    }

    @Override
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }


    @Override
    public boolean remove(Object obj) {
        if(obj == null){
            //從首節點開始遍歷,找到複合條件的節點
            for(Node<E> x = first; x.next !=null; x = x.next){
                if(x.data == null){
                    unlink(x);
                    return true;
                }
            }
        }else{
            for(Node<E> x = first; x.next !=null; x = x.next){
                if(obj.equals(x.data)){
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }


    private static class Node<E> {
        E data;//節點資料
        Node<E> prev;//上一個節點
        Node<E> next;//下一個節點

        public Node(E data, Node<E> prev, Node<E> next) {
            this.data = data;
            this.prev = prev;
            this.next = next;
        }
    }

    private String outOfBoundsMsg(int index) {
        return "Index: " + index + ", Size: " + size;
    }
}
package com.roger.collection;

public interface RogerList<E> {

    boolean add(E e);

    boolean add(int index, E e);

    E get(int index);

    int size();

    E remove(int index);

    boolean remove(Object obj);
}
package com.roger.collection.impl;

import com.roger.collection.RogerList;
import org.junit.Test;

public class RogerLinkerListTest {

    @Test
    public void testAdd() {

        RogerList<String> rogerArrayList = new RogerLinkedList<>();
        rogerArrayList.add("Roger");
        rogerArrayList.add("Mary");
        rogerArrayList.add("Bruce");
        for (int i = 0; i < rogerArrayList.size(); i++) {
            System.out.println(rogerArrayList.get(i));
        }

    }

    @Test
    public void testAddByPos() {
        RogerList<String> rogerArrayList = new RogerArrayList<String>(1);
        rogerArrayList.add("Roger");
        rogerArrayList.add("Mary");
        rogerArrayList.add("Bruce");
        rogerArrayList.add(0, "Andy");
        for (int i = 0; i < rogerArrayList.size(); i++) {
            System.out.println(rogerArrayList.get(i));
        }
    }

    @Test
    public void testRemove(){
        RogerList<String> rogerArrayList = new RogerArrayList<String>(1);
        rogerArrayList.add("Roger");
        rogerArrayList.add("Mary");
        rogerArrayList.add("Bruce");
        rogerArrayList.add(3, "Andy");


        rogerArrayList.remove(3);
        rogerArrayList.add("Bruce1");
        for (int i = 0; i < rogerArrayList.size(); i++) {
            System.out.println(rogerArrayList.get(i));
        }
    }

    @Test
    public void testRemoveByObj(){
        RogerList<String> rogerArrayList = new RogerArrayList<String>(1);
        rogerArrayList.add("Roger");
        rogerArrayList.add("Mary");
        rogerArrayList.add(null);
        rogerArrayList.add("Bruce");
        rogerArrayList.add(3, "Andy");
        rogerArrayList.add(null);

        rogerArrayList.remove("Andy");
        rogerArrayList.remove(null);
        for (int i = 0; i < rogerArrayList.size(); i++) {
            System.out.println(rogerArrayList.get(i));
        }
    }
}