面经手册 · 第8篇《LinkedList插入速度比ArrayList快?你确定吗?》

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者:小傅哥"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"博客:"},{"type":"link","attrs":{"href":"https://bugstack.cn","title":""},"content":[{"type":"text","text":"https://bugstack.cn"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"沉淀、分享、成长,让自己和他人都能有所收获!😄"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"一、前言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"你以为考你个数据结构是要造火箭?"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"🚕汽车75马力就够奔跑了,那你怎么还想要2.0涡轮+9AT呢?大桥两边的护栏你每次走的时候都会去摸吗?那怎么没有护栏的大桥你不敢上呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很多时候,你额外的能力才是自身价值的体现,不要以为你的能力就只是做个业务开发每天CRUD,并不是产品让你写CRUD,而是因为你的能力只能产品功能设计成CRUD。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"就像数据结构、算法逻辑、源码技能,它都是可以为你的业务开发赋能的,也是写出更好、更易扩展程序的根基,所以学好这份知识非常有必要。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"*本文涉及了较多的代码和实践验证图稿,欢迎关注公众号:"},{"type":"codeinline","content":[{"type":"text","text":"bugstack虫洞栈"}]},{"type":"text","text":",回复下载得到一个链接打开后,找到ID:19🤫获取!*"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"二、面试题"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"谢飞机"}]},{"type":"text","text":",ArrayList资料看了吧?嗯,那行问问你哈🦀"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**"},{"type":"codeinline","content":[{"type":"text","text":"问"}]},{"type":"text","text":"**:ArrayList和LinkedList,都用在什么场景呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**"},{"type":"codeinline","content":[{"type":"text","text":"答"}]},{"type":"text","text":"**:啊,这我知道了。ArrayList是基于数组实现、LinkedList是基于双向链表实现,所以基于数据结构的不同,遍历和查找多的情况下用ArrayList、插入和删除频繁的情况下用LinkedList。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**"},{"type":"codeinline","content":[{"type":"text","text":"问"}]},{"type":"text","text":"**:嗯,那LinkedList的插入效率一定比ArrayList好吗?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**"},{"type":"codeinline","content":[{"type":"text","text":"答"}]},{"type":"text","text":"**:对,好!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"送你个飞机✈,回去等消息吧!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其实,飞机回答的也不是不对,只是不全面。出门后不甘心买瓶"},{"type":"codeinline","content":[{"type":"text","text":"肥宅水"}]},{"type":"text","text":"又回来,跟面试官聊了2个点,要到了两张图,如下;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a0/a0859bf032ce61303febdaa319ed3787.png","alt":"小傅哥 bugstack.cn & ArrayList头插、尾插、中间","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ab/abaf829ec3c081bff9dde3d415e71b3c.png","alt":"小傅哥 bugstack.cn & LinkedList头插、尾插、中间","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如图,分别是;"},{"type":"codeinline","content":[{"type":"text","text":"10万"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"100万"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"1000万"}]},{"type":"text","text":",数据在两种集合下不同位置的插入效果,"},{"type":"text","marks":[{"type":"strong"}],"text":"所以:"},{"type":"text","text":",不能说LinkedList插入就快,ArrayList插入就慢,还需要看具体的操作情况。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来我们带着数据结构和源码,具体分析下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"三、数据结构"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Linked + List = 链表 + 列表 = LinkedList = 链表列表"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/af/af95f005c785ae68062f55ab22cf67c4.png","alt":"小傅哥 bugstack.cn & LinkedList数据结构","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LinkedList,是基于链表实现,由双向链条next、prev,把数据节点穿插起来。所以,在插入数据时,是不需要像我们上一章节介绍的ArrayList那样,扩容数组。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但,又不能说所有的插入都是高效,比如中间区域插入,他还需要遍历元素找到插入位置。具体的细节,我们在下文的源码分析中进行讲解,也帮"},{"type":"codeinline","content":[{"type":"text","text":"谢飞机"}]},{"type":"text","text":"扫除疑惑。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"四、源码分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. 初始化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"与ArrayList不同,LinkedList初始化不需要创建数组,因为它是一个链表结构。而且也没有传给构造函数初始化多少个空间的入参,例如这样是不可以的,如下;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/01/01cb71720ba60176d0be178a4fdb671a.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"但是"},{"type":"text","text":",构造函数一样提供了和ArrayList一些相同的方式,来初始化入参,如下这四种方式;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Test\npublic void test_init() {\n // 初始化方式;普通方式\n LinkedList list01 = new LinkedList();\n list01.add(\"a\");\n list01.add(\"b\");\n list01.add(\"c\");\n System.out.println(list01);\n \n // 初始化方式;Arrays.asList\n LinkedList list02 = new LinkedList(Arrays.asList(\"a\", \"b\", \"c\"));\n System.out.println(list02);\n \n // 初始化方式;内部类\n LinkedList list03 = new LinkedList()\\\\{\n {add(\"a\");add(\"b\");add(\"c\");}\n \\\\};\n System.out.println(list03);\n \n // 初始化方式;Collections.nCopies\n LinkedList list04 = new LinkedList(Collections.nCopies(10, 0));\n System.out.println(list04);\n}\n\n// 测试结果\n\n[a, b, c]\n[a, b, c]\n[a, b, c]\n[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n\nProcess finished with exit code 0"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这些方式都可以初始化操作,按需选择即可。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. 插入"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LinkedList的插入方法比较多,List中接口中默认提供的是add,也可以指定位置插入。但在LinkedList中还提供了头插"},{"type":"codeinline","content":[{"type":"text","text":"addFirst"}]},{"type":"text","text":"和尾插"},{"type":"codeinline","content":[{"type":"text","text":"addLast"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"关于插入这部分就会讲到为什么;有的时候LinkedList插入更耗时、有的时候ArrayList插入更好。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.1 头插"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先来看一张数据结构对比图,回顾下ArrayList的插入也和LinkedList插入做下对比,如下;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1a/1a6dc852f5dd31014ca4ecdb7ee3a03a.png","alt":"小傅哥 bugstack.cn & 插入对比","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看上图我们可以分析出几点;"}]},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 头插时,需要把数组元素通过"},{"type":"codeinline","content":[{"type":"text","text":"Arrays.copyOf"}]},{"type":"text","text":"的方式把数组元素移位,如果容量不足还需要扩容。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"LinkedList 头插时,则不需要考虑扩容以及移位问题,直接把元素定位到首位,接点链条链接上即可。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"2.1.1 源码"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里我们再对照下"},{"type":"codeinline","content":[{"type":"text","text":"LinkedList"}]},{"type":"text","text":"头插的源码,如下;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private void linkFirst(E e) {\n final Node f = first;\n final Node newNode = new Node<>(null, e, f);\n first = newNode;\n if (f == null)\n last = newNode;\n else\n f.prev = newNode;\n size++;\n modCount++;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"first,首节点会一直被记录,这样就非常方便头插。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"插入时候会创建新的节点元素,"},{"type":"codeinline","content":[{"type":"text","text":"new Node<>(null, e, f)"}]},{"type":"text","text":",紧接着把新的头元素赋值给first。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之后判断f节点是否存在,不存在则把头插节点作为最后一个节点、存在则用f节点的上一个链条prev链接。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最后记录size大小、和元素数量modCount。"},{"type":"text","marks":[{"type":"italic"}],"text":"modCount用在遍历时做校验,modCount != expectedModCount"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"2.1.2 验证"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"ArrayList、LinkeList,头插源码验证"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Test\npublic void test_ArrayList_addFirst() {\n ArrayList list = new ArrayList();\n long startTime = System.currentTimeMillis();\n for (int i = 0; i < 10000000; i++) {\n list.add(0, i);\n }\n System.out.println(\"耗时:\" + (System.currentTimeMillis() - startTime));\n}\n\n@Test\npublic void test_LinkedList_addFirst() {\n LinkedList list = new LinkedList();\n long startTime = System.currentTimeMillis();\n for (int i = 0; i < 10000000; i++) {\n list.addFirst(i);\n }\n System.out.println(\"耗时:\" + (System.currentTimeMillis() - startTime));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"比对结果:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/45/45619ec5a8f16282796b560aa2191acf.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里我们分别验证,10万、100万、1000万的数据量,在头插时的一个耗时情况。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如我们数据结构对比图中一样,ArrayList需要做大量的位移和复制操作,而LinkedList的优势就体现出来了,耗时只是实例化一个对象。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2 尾插"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先来看一张数据结构对比图,回顾下ArrayList的插入也和LinkedList插入做下对比,如下;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b7/b73e94871d1a781813ab9d30632a35b0.png","alt":"小傅哥 bugstack.cn & 插入对比","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看上图我们可以分析出几点;"}]},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 尾插时,是不需要数据位移的,比较耗时的是数据的扩容时,需要拷贝迁移。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"LinkedList 尾插时,与头插相比耗时点会在对象的实例化上。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"2.2.1 源码"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里我们再对照下"},{"type":"codeinline","content":[{"type":"text","text":"LinkedList"}]},{"type":"text","text":"尾插的源码,如下;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"void linkLast(E e) {\n final Node l = last;\n final Node newNode = new Node<>(l, e, null);\n last = newNode;\n if (l == null)\n first = newNode;\n else\n l.next = newNode;\n size++;\n modCount++;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"与头插代码相比几乎没有什么区别,只是first换成last"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"耗时点只是在创建节点上,"},{"type":"codeinline","content":[{"type":"text","text":"Node"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"2.2.2 验证"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"ArrayList、LinkeList,尾插源码验证"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Test\npublic void test_ArrayList_addLast() {\n ArrayList list = new ArrayList();\n long startTime = System.currentTimeMillis();\n for (int i = 0; i < 10000000; i++) {\n list.add(i);\n }\n System.out.println(\"耗时:\" + (System.currentTimeMillis() - startTime));\n}\n\n@Test\npublic void test_LinkedList_addLast() {\n LinkedList list = new LinkedList();\n long startTime = System.currentTimeMillis();\n for (int i = 0; i < 1000000; i++) {\n list.addLast(i);\n }\n System.out.println(\"耗时:\" + (System.currentTimeMillis() - startTime));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"比对结果:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/07/077b09fed550d5d1ee9883351e7f06a2.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里我们分别验证,10万、100万、1000万的数据量,在尾插时的一个耗时情况。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如我们数据结构对比图中一样,ArrayList 不需要做位移拷贝也就不那么耗时了,而LinkedList则需要创建大量的对象。"},{"type":"text","marks":[{"type":"italic"}],"text":"所以这里ArrayList尾插的效果更好一些。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.3 中间插"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先来看一张数据结构对比图,回顾下ArrayList的插入也和LinkedList插入做下对比,如下;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/79/79a25987fc75ddc43aad611e066650e7.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看上图我们可以分析出几点;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 中间插入,首先我们知道他的定位时间复杂度是O(1),比较耗时的点在于数据迁移和容量不足的时候扩容。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"LinkedList 中间插入,链表的数据实际插入时候并不会怎么耗时,但是它定位的元素的时间复杂度是O(n),所以这部分以及元素的实例化比较耗时。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"2.3.1 源码"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里看下LinkedList指定位置插入的源码;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"使用add(位置、元素)方法插入:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public void add(int index, E element) {\n checkPositionIndex(index);\n if (index == size)\n linkLast(element);\n else\n linkBefore(element, node(index));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"位置定位node(index):"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Node node(int index) {\n // assert isElementIndex(index);\n if (index < (size >> 1)) {\n Node x = first;\n for (int i = 0; i < index; i++)\n x = x.next;\n return x;\n } else {\n Node x = last;\n for (int i = size - 1; i > index; i--)\n x = x.prev;\n return x;\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"size >> 1"}]},{"type":"text","text":",这部分的代码判断元素位置在左半区间,还是右半区间,在进行循环查找。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"执行插入:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"void linkBefore(E e, Node succ) {\n // assert succ != null;\n final Node pred = succ.prev;\n final Node newNode = new Node<>(pred, e, succ);\n succ.prev = newNode;\n if (pred == null)\n first = newNode;\n else\n pred.next = newNode;\n size++;\n modCount++;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"找到指定位置插入的过程就比较简单了,与头插、尾插,相差不大。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"整个过程可以看到,插入中比较耗时的点会在遍历寻找插入位置上。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"2.3.2 验证"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"ArrayList、LinkeList,中间插入源码验证"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Test\npublic void test_ArrayList_addCenter() {\n ArrayList list = new ArrayList();\n long startTime = System.currentTimeMillis();\n for (int i = 0; i < 10000000; i++) {\n list.add(list.size() >> 1, i);\n }\n System.out.println(\"耗时:\" + (System.currentTimeMillis() - startTime));\n}\n\n@Test\npublic void test_LinkedList_addCenter() {\n LinkedList list = new LinkedList();\n long startTime = System.currentTimeMillis();\n for (int i = 0; i < 1000000; i++) {\n list.add(list.size() >> 1, i);\n }\n System.out.println(\"耗时:\" + (System.currentTimeMillis() - startTime));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"比对结果:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ff/ff7c74924eb6b3c97e9f8a328f8029db.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里我们分别验证,10万、100万、1000万的数据量,在中间插时的一个耗时情况。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到Linkedlist在中间插入时,遍历寻找位置还是非常耗时了。所以不同的情况下,需要选择不同的List集合做业务。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3. 删除"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"讲了这么多插入的操作后,删除的知识点就很好理解了。与ArrayList不同,删除不需要拷贝元素,LinkedList是找到元素位置,把元素前后链连接上。基本如下图;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/91/91702b06e1c6e5e4874819b5d313b962.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"确定出要删除的元素x,把前后的链接进行替换。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果是删除首尾元素,操作起来会更加容易,这也就是为什么说插入和删除快。但中间位置删除,需要遍历找到对应位置。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3.1 删除操作方法"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ff/ff6dfc78120564bc512e2f6f936527af.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"源码:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Test\npublic void test_remove() {\n LinkedList list = new LinkedList();\n list.add(\"a\");\n list.add(\"b\");\n list.add(\"c\");\n \n list.remove();\n list.remove(1);\n list.remove(\"a\");\n list.removeFirst();\n list.removeLast();\n list.removeAll(Arrays.asList(\"a\", \"b\"));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3.2 源码"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"删除操作的源码都差不多,分为删除首尾节点与其他节点时候,对节点的解链操作。这里我们举例一个删除其他位置的源码进行学习,如下;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"list.remove(\"a\");"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public boolean remove(Object o) {\n if (o == null) {\n for (Node x = first; x != null; x = x.next) {\n if (x.item == null) {\n unlink(x);\n return true;\n }\n }\n } else {\n for (Node x = first; x != null; x = x.next) {\n if (o.equals(x.item)) {\n unlink(x);\n return true;\n }\n }\n }\n return false;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这一部分是元素定位,和"},{"type":"codeinline","content":[{"type":"text","text":" unlink(x)"}]},{"type":"text","text":"解链。循环查找对应的元素,这部分没有什么难点。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"unlink(x)解链"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"E unlink(Node x) {\n // assert x != null;\n final E element = x.item;\n final Node next = x.next;\n final Node prev = x.prev;\n \n if (prev == null) {\n first = next;\n } else {\n prev.next = next;\n x.prev = null;\n }\n \n if (next == null) {\n last = prev;\n } else {\n next.prev = prev;\n x.next = null;\n }\n \n x.item = null;\n size--;\n modCount++;\n return element;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这部分源码主要有以下几个知识点;"}]},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"获取待删除节点的信息;元素item、元素下一个节点next、元素上一个节点prev。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"如果上个节点为空则把待删除元素的下一个节点赋值给首节点,否则把待删除节点的下一个节点,赋值给待删除节点的上一个节点的子节点。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"同样待删除节点的下一个节点next,也执行2步骤同样操作。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"最后是把删除节点设置为null,并扣减size和modeCount数量。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4. 遍历"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来说下遍历,ArrayList与LinkedList的遍历都是通用的,基本包括5种方式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里我们先初始化出待遍历的集合1千万数据;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"int xx = 0;\n@Before\npublic void init() {\n for (int i = 0; i < 10000000; i++) {\n list.add(i);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"4.1 普通for循环"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Test\npublic void test_LinkedList_for0() {\n long startTime = System.currentTimeMillis();\n for (int i = 0; i < list.size(); i++) {\n xx += list.get(i);\n }\n System.out.println(\"耗时:\" + (System.currentTimeMillis() - startTime));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"4.2 增强for循环"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Test\npublic void test_LinkedList_for1() {\n long startTime = System.currentTimeMillis();\n for (Integer itr : list) {\n xx += itr;\n }\n System.out.println(\"耗时:\" + (System.currentTimeMillis() - startTime));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"4.3 Iterator遍历"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Test\npublic void test_LinkedList_Iterator() {\n long startTime = System.currentTimeMillis();\n Iterator iterator = list.iterator();\n while (iterator.hasNext()) {\n Integer next = iterator.next();\n xx += next;\n }\n System.out.println(\"耗时:\" + (System.currentTimeMillis() - startTime))\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"4.4 forEach循环"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Test\npublic void test_LinkedList_forEach() {\n long startTime = System.currentTimeMillis();\n list.forEach(integer -> {\n xx += integer;\n });\n System.out.println(\"耗时:\" + (System.currentTimeMillis() - startTime));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"4.5 stream(流)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Test\npublic void test_LinkedList_stream() {\n long startTime = System.currentTimeMillis();\n list.stream().forEach(integer -> {\n xx += integer;\n });\n System.out.println(\"耗时:\" + (System.currentTimeMillis() - startTime));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"那么"},{"type":"text","text":",以上这5种遍历方式谁最慢呢?按照我们的源码学习分析下吧,欢迎留下你的答案在评论区!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"五、总结"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList与LinkedList都有自己的使用场景,如果你不能很好的确定,那么就使用ArrayList。但如果你能确定你会在集合的首位有大量的插入、删除以及获取操作,那么可以使用LinkedList,因为它都有相应的方法;"},{"type":"codeinline","content":[{"type":"text","text":"addFirst"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"addLast"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"removeFirst"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"removeLast"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"getFirst"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"getLast"}]},{"type":"text","text":",这些操作的时间复杂度都是O(1),非常高效。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LinkedList的链表结构不一定会比ArrayList节省空间,首先它所占用的内存不是连续的,其次他还需要大量的实例化对象创造节点。虽然不一定节省空间,但链表结构也是非常优秀的数据结构,它能在你的程序设计中起着非常优秀的作用,例如可视化的链路追踪图,就是需要链表结构,并需要每个节点自旋一次,用于串联业务。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"程序的精髓往往就是数据结构的设计,这能为你的程序开发提供出非常高的效率改变。可能目前你还不能用到,但万一有一天你需要去造🚀火箭了呢?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"六、系列文章"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://bugstack.cn/interview/2020/08/27/%E9%9D%A2%E7%BB%8F%E6%89%8B%E5%86%8C-%E7%AC%AC7%E7%AF%87-ArrayList%E4%B9%9F%E8%BF%99%E4%B9%88%E5%A4%9A%E7%9F%A5%E8%AF%86-%E4%B8%80%E4%B8%AA%E6%8C%87%E5%AE%9A%E4%BD%8D%E7%BD%AE%E6%8F%92%E5%85%A5%E5%B0%B1%E6%8A%8A%E8%B0%A2%E9%A3%9E%E6%9C%BA%E9%9D%A2%E6%99%95%E4","title":""},"content":[{"type":"text","text":"ArrayList也这么多知识?一个指定位置插入就把谢飞机面晕了!"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://bugstack.cn/interview/2020/08/07/%E9%9D%A2%E7%BB%8F%E6%89%8B%E5%86%8C-%E7%AC%AC3%E7%AF%87-HashMap%E6%A0%B8%E5%BF%83%E7%9F%A5%E8%AF%86-%E6%89%B0%E5%8A%A8%E5%87%BD%E6%95%B0-%E8%B4%9F%E8%BD%BD%E5%9B%A0%E5%AD%90-%E6%89%A9%E5%AE%B9%E9%93%BE%E8%A1%A8%E6%8B%86%E5%88%86-%E6%B7%B1%E5%BA%A6%E5%AD%A6%E","title":""},"content":[{"type":"text","text":"HashMap核心知识,扰动函数、负载因子、扩容链表拆分,深度学习"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://bugstack.cn/itstack-code-life/2020/04/11/%E5%B7%A5%E4%BD%9C%E4%B8%A4%E5%B9%B4%E7%AE%80%E5%8E%86%E5%86%99%E6%88%90%E8%BF%99%E6%A0%B7-%E8%B0%81%E8%A6%81%E4%BD%A0%E5%91%80.html","title":""},"content":[{"type":"text","text":"简历写成这样,谁要你呀!"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://bugstack.cn/itstack-code-life/2020/03/31/%E5%A4%A7%E5%AD%A6%E5%9B%9B%E5%B9%B4%E5%88%B0%E6%AF%95%E4%B8%9A%E5%B7%A5%E4%BD%9C5%E5%B9%B4%E7%9A%84%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF%E8%B5%84%E6%BA%90%E6%B1%87%E6%80%BB.html","title":""},"content":[{"type":"text","text":"工作5年的学习路线资源汇总"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://bugstack.cn/itstack-code-life/2020/07/25/12%E5%A4%A9-%E8%BF%99%E6%9C%AC-%E9%87%8D%E5%AD%A6Java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-PDF%E4%B9%A6%E7%B1%8D%E4%B8%8B%E8%BD%BD%E9%87%8F9k-%E6%96%B0%E5%A2%9E%E7%B2%89%E4%B8%9D1400%E4%BA%BA-Github%E4%B8%8A%E5%85%A8%E7%90%83%E6%8E%A8%E8%8D%90%E6%A6%9C","title":""},"content":[{"type":"text","text":"懂设计模式才能面试到20k以上"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章