JDK源码学习笔记(集合篇 - LinkedList)

LinkedList -> AbstractSequentialList -> List
同时实现了接口Deque, Cloneable, Serializable

书同上文,LinkedList就是上学时学的链表,很多公司,比如华为的应届基础面试题很多就是考的这个,比如链表反转,双向链表等。Java openJDK里的LinkedList理念上和这个并没有本质区别,从继承结构可以看出,这个LinkedList实现了List接口,那就是有序的线性结构,同时也实现了Deque(读音,待客)就是双向链表的意思,有head有tail,根据不同场景可以当队列和栈来用。当然它的插入删除都比数组Array的开销要小很多,毕竟就只有一些指针的指向变动,而数组是要真的复制数据,移动数据,有较重的内存拷贝等操作,缺点也很明显,无法RandomAccess,不可能像数组一样做到指哪打哪。这个来看看它是如何实现的,捡重点看。

构造 - Constructor

transient int size = 0;
transient Node<E> first;
transient Node<E> last;

内部有三个变量,大小size,head首元素first和tail元素last,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;
    }
}

一个私有静态内部类,方便内部的数组结构组织,其实也很简单,就三个要素,节点的信息item,前一个节点指针prev,后一个节点指针next。
两个构造函数,一个无参构造器和一个集合构造器签名为addAll(Collection<? extends E> c),无参构造器什么也没有不谈了。重点看下这个传入一个Collection的构造器,里面调用的addAll(size,c)。这个函数的意思就是从size所在的index开始把集合c加入到LinkedList中。
要做到这点,首先要定位到这个index所指向的元素,这里源码里用了一个位操作的技巧。

Node<E> node(int index) {
    if (index < (size >> 1)) {
        Node<E> x = first;
        // Iterate & Search
    } else {
        Node<E> x = last;
        // Iterate & Search
    }
}

size >> 1意思就是把size变为2进制然后往右移一位,也就是除以2,这个其实就是为了判断这个index是更靠近head还是tail,如果小于中间的index,那就从head找,否则从tail找。找到这个node后,后面就很简单了,把要插入的node的prev指向这个节点的prev,以此类推可以把整个collection插入进去,其实就是头插法,最后把collection的最后一个元素的next指向之前找到的这个node,然后再把node的prev指向collection里的最后一个元素,完成。整个LinkedList的size和modCount都要变,size变为原大小加collection的大小之和,modCount加一。这里再说下这个modCount,它存在的目的就是为了避免多个修改集合的操作导致脏读,设计原则是fast-fail,如果在遍历期间有其他操作改变了这个集合,那么在下次取操作时立刻抛出异常告诉用户这个集合改变了,无法继续遍历。

增 - Add

一共三个方法

public boolean add(E e)
public void add(int index, E element)
public void addFirst(E e)
public void addLast(E e)
public boolean addAll(Collection<? extends E> c)

这里addAll因为之前提过了就不再进一步讨论了,add(E e)有两个版本一个是尾插法,直接把node插入到尾部,也可以利用addAll,传入尾部的index做插入。add(index,element)如果index不是size的话,就在这个元素之前做插入,如果是size的话就在tail后再插入一个新节点。addFirst就是头插法,在head之前插入一个节点,然后把这个节点变为head,addLast与之相反。

删 - Remove

public E remove()

默认删除head元素,如果有的话把head下一个元素变为head,同时把head的prev和next都指向null,帮助GC回收内存。

E remove(int index)

删除指定index的元素,利用之前提到的方法定位到元素,再用同样的方法把该节点prev和next都置null。

public boolean remove(Object o)

如果object为null,删除第一个item为null的节点,如果不为空,则删除满足equals(object)的节点。
还有removeFirst和removeLast,其实就是判断head和tail,如果不为空则删除。至于像removeFirstOccurrence和removeLastOccurrence,一个从前往后找,一个从后往前找,找打就删掉该节点。

改 - update/replace

LinkedList里没有replace方法,也没有update方法,但有set方法。当然你也可以创建一个新节点,然后把这个节点插入进去并删除原有的节点。或者找到要修改的节点,然后修改节点内部的item。还有个replaceAll,来自List接口的default实现,传入一个UnaryOperator,对集合的所有元素做一个mapping转换操作。set方法原理类似,只不过需要指定下标index,然后用新节点替换这个index所在的节点。

查 - get/read

public E get(int index)

利用前文提到的node方法,找到指定下标的节点并返回。
getFirst和getLast原理很简单,这里不赘述了,就是返回head和tail,如果为null就抛出NoSuchElementException。

offer & poll

生产者消费者模型,相关的方法还有peek,take,remove,add,put等。叫法大同小异。因为LinkedList实现了Deque,所以是有头有尾的双向链表,生产者可以不停的做尾插法往tail加入node,消费者可以不停的从head取走并删除元素。offer就是生产者,提供一个node,poll就是消费者,取走一个node。在LinkedList里的offer和poll是非阻塞的,如果call的时候没有元素可以poll那么就返回null。当然也可以继承这个LinkedList把这个改造成阻塞的版本,加一个timeout即可。后面在讲BlockingQueue的时候会细讲,这里不展开。

push & pop

典型的栈操作,默认实现是从头部插入,从头部删除,这里不赘述了。

以上就是LinkedList里所有的比较重要的方法和用法。

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