LinkedList 部分源码分析


LinkedList的源码解析


public class LinkedList<E>

    extends AbstractSequentialList<E>

    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

{

    transient int size = 0;  // transient不可序列化


    transient Node<E> first;

    transient Node<E> last;




构造方法:

    public LinkedList() {

    }

    public LinkedList(Collection<? extends E> c) {

        this();

        addAll(c);

    }


//内部私有类Node:

  构造方法的参数顺序是:前继节点的引用,数据,后继节点的引用。

    关于为什么Node这个类是静态的?答案是:这跟内存泄露有关,Node类是在LinkedList类中的,也就是一个内部类,若不使用static修饰,那么Node就是一个普通的内部类,在java中,一个普通内部类在实例化之后,默认会持有外部类的引用,这就有可能造成内存泄露。但使用static修饰过的内部类(称为静态内部类),不用再创建外部类的对象了,就不会有这种问题


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 方法


//addFirst 执行linkFirst方法

public void addFirst(E e) {

        linkFirst(e);

}


//addLast add执行的都是同一个方法

public void addLast(E e) {

        linkLast(e);

    }

public boolean add(E e) {

        linkLast(e);

        return true;

}



add(index,element)

//在某个索引位置添加元素

//add方法将添加集合元素分为2种情况,

//一种是在集合尾部添加,另一种是在集合中间或头部添加,

public void add(int index, E element) {

//调用方法去检查输入的索引是否存在,不在就抛异常

        checkPositionIndex(index);


//如果你所输入的索引值刚好等于集合的大小,就属于把元素插入到链表尾部,索引从0开始

        if (index == size)

            linkLast(element);

        else

//先调用node(int index)方法得到指定位置的元素节点,也就是linkBefore()方法中的形参 succ

            linkBefore(element, node(index));

    }







addAll 方法

public boolean addAll(Collection<? extends E> c) {

        return addAll(size, c);

    }

public boolean addAll(int index, Collection<? extends E> c) {

        checkPositionIndex(index);  // 检查是否越界

        Object[] a = c.toArray();  // 将集合转为数组

        int numNew = a.length;    // 获取到集合的大小

        if (numNew == 0)      //长度为空返回false

            return false;

        Node<E> pred, succ;   // pred:中间变量, succ:插入位置的后一个元素

        if (index == size) {  // 如果插入位置为尾元素的下一个位置

            succ = null;       // 插入位置后面没有元素了

            pred = last;       // 新插入集合的前一个元素为原来的尾元素

        } else {              // 如果不是在尾部的下一个元素插入

            succ = node(index); // 通过node()方法获取到index位置的元素

            pred = succ.prev;  // 插入集合的首元素的前指向为index位置的前一个元素

        }

        for (Object o : a) {  // 循环遍历,使新插入集合元素的排列起来,并使pred指向新插入集合的最后一个元素

            @SuppressWarnings("unchecked") E e = (E) o;

            Node<E> newNode = new Node<>(pred, e, null);

            if (pred == null)

                first = newNode;

            else

                pred.next = newNode;

            pred = newNode;

        }


        if (succ == null) {   // 如果插入位置后一个元素为空,说明是尾部

            last = pred;       // 则尾元素置为插入集合的尾元素

        } else {

            pred.next = succ;  // 否则将插入集合的尾元素后指向为插入位置的下一个元素

            succ.prev = pred;  // 插入位置的下一个元素的前指向为插入集合的尾元素

        }


        size += numNew;

        modCount++;

        return true;

    }







LinkFirst

//linkFirst方法是私有的所以不能从外部调用

该方法,从外部传入我们要add的参数

private void linkFirst(E e) {

//让此时集合中的头节点(即first"指针"指向的节点)赋给变量f

        final Node<E> f = first;  


//创建一个新节点,结合Node的构造函数,我们可以知道,在创建新节点(newNode)的同时,newNodenext指向了f(即之前集合中的头节点),变量f 就是newNode的后驱节点了,newNode的前继节点为null

        final Node<E> newNode = new Node<>(null, e, f);

//再将first指向newNode,也就是说newNode成为该链表新的头节点

        first = newNode;

        if (f == null)

//接着,判断变量f 是否为null,若是null,说明之前集合中没有元素(此时newNode是集合中唯一一个元素),则将last指向newNode,也就是说此时的newNode既是头节点又是尾节点(要知道,这时newNode中的prevnext均是null,但被firstlast同时指向);若变量f 不是null,说明之前集合中已经存在了至少一个元素,则让之前集合中的头节点(即变量f )的prev指向newNode。(结合步骤2,此时的newNodenewNode的后驱节点f已经是相互指向了)

            last = newNode;

        else

            f.prev = newNode;

        size++;

        modCount++;    //理解为修改的次数

    }



linkLast方法


 //让此时集合中的尾节点(即last"指针"指向的节点)赋给变量l

    void linkLast(E e) {

        final Node<E> l = last;


//创建一个新节点,结合Node的构造函数,我们可以知道,在创建新节点(newNode)的同时,newNodeprev指向了l(即之前集合中的尾节点),变量 l 就是newNode的前驱节点了,newNode的后继节点为null

        final Node<E> newNode = new Node<>(l, e, null);

//再将last指向newNode,也就是说newNode成为该链表新的末尾节点。

        last = newNode;

//接着,判断变量 l 是否为null,若是null,说明之前集合中没有元素(此时newNode是集合中唯一一个元素),则将first指向newNode,也就是说此时的newNode既是头节点又是尾节点(要知道,这时newNode中的prevnext均是null,但被firstlast同时指向);若变量 l 不是null,说明之前集合中已经存在了至少一个元素,则让之前集合中的尾节点(即变量 l )的next指向newNode。(结合步骤2,此时的newNodenewNode的前驱节点 l 已经是相互指向了)

        if (l == null)

            first = newNode;

        else

            l.next = newNode;

        size++;

        modCount++;

}



LinkBefore方法


    void linkBefore(E e, Node<E> succ) {

        // assertsucc != null;

//通过succ.prevt得到succ的前一个元素pred。(此时拿到了第index个元素succ,和第index-1个元素pred

        final Node<E> pred = succ.prev;

//再创建一个新节点newNodenewNodeprev指向了prednewNodenext指向了succ。(即newNodesuccpred的中间插入,并单向与它们分别建立联系,egpred ← newNode → succ

        final Node<E> newNode = new Node<>(pred, e, succ);

//再让succprev指向newNode。(succ与跟newNode建立联系了,此时succnewNode是双向关联,egpred ← newNode ? succ)。

        succ.prev = newNode;

//接着,判断pred是否为null,若是null,说明之前succ是集合中的第一个元素(即index值为0),现在newNode跑到了succ前面,所以只需要将first指向newNodeegfirst ? newNode ?succ;pred不为null,则将prednext指向newNode。(这时pred也主动与newNode建立联系了,此时prednewNode也是双向关联,egpred?newNode ? succ

        if (pred == null)

            first = newNode;

        else

            pred.next = newNode;

        size++;

        modCount++;

    }





remove方法

   //removeFirst方法

//获取到LinkedList的首元素,然后判断是否为空,如果为空,则抛出NoSuchElementException异常,否则通过调用unlinkFirst(Node<E> f)方法来移除首元素。

unlinkFirst这个方法是LinkedList的内部方法,源代码如下。首先拿到首元素的下一个元素(新的首元素),然后将首元素的值,和指向下一个元素的变量都置为空,然后等待垃圾回收器空闲的时候把首元素GC掉,然后判断新的首元素是否为空,如果为空,则置尾元素也为空,并且新的首元素的前指向也置空,size(列表存储个数)减一,modeCount(结构变化次数)也减一,返回新的首元素。

    public E removeFirst() {

        final Node<E> f = first;

        if (f == null)   

            throw new NoSuchElementException();

      return unlinkFirst(f);  // 调用unlinkFirst(Node<E>f)来移除首元素

    }

    public E removeLast() {

        final Node<E> l = last;

        if (l == null)

            throw new NoSuchElementException();

        return unlinkLast(l);

    }


    private E unlinkFirst(Node<E> f) {

        // assert f

== first && f != null;

        final E element = f.item;

        final Node<E> next = f.next;

        f.item = null;

        f.next = null; // help GC

        first = next;

        if (next == null)

            last = null;

        else

            next.prev = null;

        size--;

        modCount++;

        return element;

    }

    private E unlinkLast(Node<E> l) {

        // assert l

== last && l != null;

        final E element = l.item;

        final Node<E> prev = l.prev;

        l.item = null;

        l.prev = null; // help GC

        last = prev;

        if (prev == null)

            first = null;

        else

            prev.next = null;

        size--;

        modCount++;

        return element;

    }


//首先通过查找是否包含该元素,实现原理和contains方法一致,在找到的时候,调用unlink方法,移除元素。

unlink方法就是将,要移除元素的前一个元素的后指针指向移除元素的下一个元素,要移除元素的下一个元素的前指针指向要移除元素的上一个元素,当然,要判断前后元素是否为空的状态。

 

public boolean remove(Object o) {

        if (o == null) {

            for (Node<E> x = first; x != null; x = x.next) {

                if (x.item == null) {

                   unlink(x);

                   return true;

                }

            }

        } else {

            for (Node<E> x = first; x != null; x = x.next) {

                if (o.equals(x.item)) {

                   unlink(x);

                   return true;

                }

            }

        }

        return false;

    }



E unlink(Node<E> x) {

        // assert x

!= null;

        final E element = x.item;

        final Node<E> next = x.next;

        final Node<E> prev = x.prev;


        if (prev == null) {

            first = next;

        } else {

            prev.next = next;  // 要移除元素的上一个元素的后指针指向要移除元素的下一个元素

            x.prev = null;

        }


        if (next == null) {   // 要移除元素的下一个元素的后指针指向要移除元素的上一个元素

            last = prev;

        } else {

            next.prev = prev;

            x.next = null;

        }


        x.item = null;

        size--;

        modCount++;

        return element;

    }


public E remove(int index) {

        checkElementIndex(index);

        return unlink(node(index));

    }



get方法

public E get(int index) {

//先查看索引是否越界

        checkElementIndex(index);

        return node(index).item;

    }



    public E getFirst() {

        final Node<E> f = first;

        if (f == null)

            throw new NoSuchElementException();

        return f.item;

    }

    public E getLast() {

        final Node<E> l = last;

        if (l == null)

            throw new NoSuchElementException();

        return l.item;

    }

Set方法

public E set(int index, E element) {

        checkElementIndex(index);

        Node<E> x = node(index);

        E oldVal = x.item;

        x.item = element;

        return oldVal;

    }




node方法

//根据索引值查找相应的Node

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;

        }

    }


   clear方法


  //通过遍历链表,将每一个结点的结构体内的变量置为空,等待垃圾处理器进行回收,并置size0 

    public void clear() {

        // Clearing

all of the links between nodes is "unnecessary", but:

        // - helps a

generational GC if the discarded nodes inhabit

        //   more than one generation

        // - is sure

to free memory even if there is a reachable Iterator

        for (Node<E> x = first; x != null; ) {

            Node<E> next = x.next;

            x.item = null;

            x.next = null;

            x.prev = null;

            x = next;

        }

        first = last = null;

        size = 0;

        modCount++;

    }



//查看是否包含该元素

public boolean contains(Object o) {

        return indexOf(o) != -1;

    }



//如果传入的参数为空,遍历是只要得到的item为空的时候就返回当前的index,如果不为空,遍历是比较器值

public int indexOf(Object o) {

        int index = 0;

        if (o == null) {

            for (Node<E> x = first; x != null; x = x.next) {

                if (x.item == null)

                   return index;

                index++;

            }

        } else {

            for (Node<E> x = first; x != null; x = x.next) {

                if (o.equals(x.item))

                   return index;

                index++;

            }

        }

        return -1;

    }



 public int size() {

        return size;

    }




    private boolean isElementIndex(int index) {

        return index >= 0 && index < size;

    }

    private boolean isPositionIndex(int index) {

        return index >= 0 && index <= size;

    }

    private String outOfBoundsMsg(int index) {

        return "Index:

"+index+", Size: "+size;

    }


    private void checkElementIndex(int index) {

        if (!isElementIndex(index))

            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    }


    private void checkPositionIndex(int index) {

        if (!isPositionIndex(index))

            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    }



    lastIndex方法

    //返回此列表中最后出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1。

    public int lastIndexOf(Object o) {

        int index = size;

   //如果传入的值为空,遍历集合,从后往前遍历,如果得到的item为空就刚好就是那个地方所对应的index

        if (o == null) {

            for (Node<E> x = last; x != null; x = x.prev) {

                index--;

                if (x.item == null)

                   return index;

            }

        } else {

            for (Node<E> x = last; x != null; x = x.prev) {

                index--;

                if (o.equals(x.item))

                   return index;

            }

        }

        return -1;

    }



    public E peek() {

        final Node<E> f = first;

        return (f == null) ? null : f.item;

    }



    public E element() {

        return getFirst();

    }



    public E poll() {

        final Node<E> f = first;

        return (f == null) ? null : unlinkFirst(f);

    }

    public E remove() {

        return removeFirst();

    }



    public boolean offer(E e) {

        return add(e);

    }



    public boolean offerFirst(E e) {

        addFirst(e);

        return true;

    }

    public boolean offerLast(E e) {

        addLast(e);

        return true;

    }

    public E peekFirst() {

        final Node<E> f = first;

        return (f == null) ? null : f.item;

     }

    public E peekLast() {

        final Node<E> l = last;

        return (l == null) ? null : l.item;

    }

    public E pollFirst() {

        final Node<E> f = first;

        return (f == null) ? null : unlinkFirst(f);

    }



    public E pollLast() {

        final Node<E> l = last;

        return (l == null) ? null : unlinkLast(l);

    }



    public void push(E e) {

        addFirst(e);

    }



    public E pop() {

        return removeFirst();

    }



    public boolean removeFirstOccurrence(Object o) {

        return remove(o);

    }



    public boolean removeLastOccurrence(Object o) {

        if (o == null) {

            for (Node<E> x = last; x != null; x = x.prev) {

                if (x.item == null) {

                   unlink(x);

                   return true;

                }

            }

        } else {

            for (Node<E> x = last; x != null; x = x.prev) {

                if (o.equals(x.item)) {

                   unlink(x);

                   return true;

                }

            }

        }

        return false;

    }

    public ListIterator<E> listIterator(int index) {

        checkPositionIndex(index);

        return new ListItr(index);

    }


}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容