【Java系列008】和你一起揭开LinkedList的“庐山真面目”
2021-02-13 03:17
标签:结构 add bsp 注意 top 检验 close cdb running 你好!我是miniluo,又到了周末,我将和你一起推翻认知里的LinkedList,它并非如教科书所言那么的高效。 JDK集合类List,我们最常用的莫过于ArrayList。而面试官也是最常考察应聘者对ArrayList和LinkedList的理解,其实考察的是应聘者对数据结构的掌握程度。 我们知道两者代表两种不同数据结构,分别是数组和(双向)链表。数据结构通常都是用大O时间复杂度来比对,通过下图(图片来源:https://www.bigocheatsheet.com/),我们可以看到数组和链表两者之间大O的显著差异: 总结就两点: 1、对于数组,随机元素访问的时间复杂度是 O(1),元素插入操作是 O(n)。 2、对于链表,按偏移量访问的时间复杂度是 O(n),元素插入操作是 O(1)。 而我们从教科书中可以知道,链表插入速度快,适用于频繁插入,较少随机读取的场景中。那LinkedList真的如教科书所言?如我们所想吗?“实践是检验真理的唯一标准”,没有实践就下断言的结论是耍流氓,不具备科学性;秉着这个思想,我们撸一波代码验证一番。 元素访问时间复杂度 我们首先来看两者的随机访问时间复杂度,看看下文代码: 我们指定elementCount和loopCount分别都是10w,在main函数执行测试代码。 执行结果: 从执行结果看,确实如我对数组和链表两种数据结构的认识一样,LinkedList耗时4秒左右,而ArrayList只需0.1秒。好,接下来我们试试插入操作时间复杂度是否真的如教科书所说那样。 元素随机插入时间复杂度 我们新增下面2个方法,之后也是分别设置elementCount和loopCount都是10w。 在main函数执行测试代码 此时……,你可以去喝几口茶再回来…… 天呐(Oh,my god),LinkedList竟然耗时32秒,ArrayList只需要1.6秒,居然让我们大跌眼镜?不是说LinkedList的插入比ArrayList快? 真相是…… 我们进入LinkedList的源码,找到add()的方法,其中一个是 1 /**
2 * ArrayList访问
3 * @param elementCount 元素个数
4 * @param loopCount 循环次数
5 */
6 private static void arrayListGet(int elementCount, int loopCount) {
7 List
1int elementCount = 100000;
2int loopCount = 100000;
3StopWatch stopWatch = new StopWatch();
4stopWatch.start("linkedListGet");
5linkedListGet(elementCount, loopCount);
6stopWatch.stop();
7stopWatch.start("arrayListGet");
8arrayListGet(elementCount, loopCount);
9stopWatch.stop();
10System.out.println(stopWatch.prettyPrint());
1StopWatch ‘‘: running time (millis) = 4036
2-----------------------------------------
3ms % Task name
4-----------------------------------------
504024 100% linkedListGet
600012 000% arrayListGet
1 /**
2 * ArrayList插入
3 * @param elementCount 元素个数
4 * @param loopCount 循环次数
5 */
6 private static void arrayListAdd(int elementCount, int loopCount) {
7 List
1int elementCount = 100000;
2int loopCount = 100000;
3StopWatch stopWatchAdd = new StopWatch();
4stopWatchAdd.start("linkedListAdd");
5linkedListAdd(elementCount, loopCount);
6stopWatchAdd.stop();
7stopWatchAdd.start("arrayListAdd");
8arrayListAdd(elementCount, loopCount);
9stopWatchAdd.stop();
10System.out.println(stopWatchAdd.prettyPrint());
1StopWatch ‘‘: running time (millis) = 34540
2-----------------------------------------
3ms % Task name
4-----------------------------------------
532911 095% linkedListAdd
601629 005% arrayListAdd
1 public void add(int index, E element) {
2 checkPositionIndex(index);
3
4 if (index == size)
5 linkLast(element);
6 else
7 linkBefore(element, node(index));
8 }
9
10Node
看第7行node(index),再看node()方法的实现,也就是我们要完成随机插入,则首先需要循环遍历找到index所在节点。循环遍历,那不也是O(n)。
为何我们会认为LinkedList的插入时间复杂度是O(1)呢,这是因为如果直接在链表尾部或首部,以及知道了插入节点则其插入时间复杂度确实是O(1)。因此在实际项目中使用应该要注意这个问题,以免自认为对的实际上导致了程序执行效率低下的结果。今天的案例已同步GitHub(https://github.com/littleluo/bj-share-java.git)。
好了下面,我们总结下今天的内容。
总结
今天,和你一起写了一段代码验证ArrayList和LinkedList的随机访问和随机插入时间复杂度的结论,随机访问事实上如教科书所说那样,但是随机插入则让我们大跌眼镜。这个结论告诉我们不要凡事“想当然”,该用实践证明的不能偷懒,多看源码的实现过程,以免踩雷。另外我们测试案例用到了JDK1.8的特性IntStream,如果对此不熟悉的同学可以先去看看IntStream,有助于你读懂案例代码。
思考和讨论
1、文中关于各种数据结构的对别图,我们只举了数组和链表进行了实际比对,有兴趣的同学可以写代码看看其他数据结构是否真的如图所述呢?
2、实际项目中你有用过LinkedList吗?
欢迎留言与我分享和指正!也欢迎你把这篇文章分享给你的朋友或同事,一起交流。
感谢您的阅读,我们下节再见!
来源:站长
【Java系列008】和你一起揭开LinkedList的“庐山真面目”
标签:结构 add bsp 注意 top 检验 close cdb running
原文地址:https://www.cnblogs.com/1994jinnan/p/12728617.html
文章标题:【Java系列008】和你一起揭开LinkedList的“庐山真面目”
文章链接:http://soscw.com/index.php/essay/54712.html