浅谈普通for循环遍历LinkedList弊端
发布时间 - 2026-01-10 22:36:22 点击率:次java开发过程中,用到的最多的List集合就属ArrayList与LinkedList。对于ArrayList的遍历,通常是下面的方法:
public static void main(String[] args)
{
List<Integer> arrayList =
new ArrayList<Integer>();
for (int i = 0; i < 100; i++)
arrayList.add(i);
for (int i = 0; i < 100; i++)
System.out.println(arrayList.get(i));
}
假如集合换成LinkedList,可能我们就会用相同得方法进行遍历,如下:
public static void main(String[] args)
{
List<Integer> linkedList =
new LinkedList<Integer>();
for (int i = 0; i < 100; i++)
linkedList.add(i);
for (int i = 0; i < 100; i++)
System.out.println(linkedList.get(i));
}
请记住:这是一种非常糟糕的做法。这其实已经不是Java的问题,而是数据结构的问题了,我相信语言从Java换成其他的也都一样。
下面对ArrayList和LinkedList的普通for循环效率进行测试以及分析原因。
ArrayList和LinkedList使用普通for循环遍历速度对比
先给出测试代码:
public class ListIteratorTest
{
private final static int LIST_SIZE = 1000;
public static void main(String[] args)
{
List<Integer> arrayList =
new ArrayList<Integer>();
List<Integer> linkedList =
new LinkedList<Integer>();
for (int i = 0; i < LIST_SIZE; i++)
{
arrayList.add(i);
linkedList.add(i);
}
long startTime = System.currentTimeMillis();
for (int i = 0; i < arrayList.size(); i++)
arrayList.get(i);
System.out.println("ArrayList遍历速度:" + (System.currentTimeMillis() - startTime) + "ms");
startTime = System.currentTimeMillis();
for (int i = 0; i < linkedList.size(); i++)
linkedList.get(i);
System.out.println("LinkedList遍历速度:" + (System.currentTimeMillis() - startTime) + "ms");
}
}
不断增大LIST_SIZE,我用表格表示一下运行结果:
| 1000 | 5000 | 10000 | 50000 | 100000 | |
| ArrayList | 0ms | 1ms | 2ms | 3ms | 3ms |
| LinkedList | 3ms | 16ms | 88ms | 2446ms | 18848ms |
下面解释一下产生此现象的原因。从运行结果我们看到,按倍数增大List容量,ArrayList的遍历显得比较稳定,而LinkedList的遍历几乎是爆发式的增长,再测试下去已经没有必要了。
ArrayList使用普通for循环遍历快的原因
先看一下ArrayList的get方法源代码:
public E get(int index) {
RangeCheck(index);
return (E) elementData[index];
}
看到ArrayList的get方法只是从数组里面拿一个位置上的元素罢了。我们有结论,ArrayList的get方法的时间复杂度是O(1),O(1)的意思也就是说时间复杂度是一个常数,和数组的大小并没有关系,只要给定数组的位置,直接就能定位到数据。
其实熟悉C、C++或者对指针理解的朋友一定很好理解为什么,我解释一下为什么对数组使用get就快。
在计算机底层,数据都是有地址的,就像人有住址一样。假设我写了这么一句代码:
int[3] ints = {1, 3, 5};
在Java中一个int型数据是4个字节,此时计算机内部做的事情是,在内存空间中找到一块连续的、足以存放3个4字节也就是12字节的数组的内存空间,并返回该内存空间的首地址。比方说该内存空间的首地址是0x00吧,那么那么1就放在0x00~0x03中、3就放在0x04~0x07中、5就放在0x08~0x0B中。
这时就很简单了,取ints[1]的时候,计算机就会算出ints[1]的数据是存放在以0x04开头,占据4个字节空间的内存中,因此,计算机会从0x04~0x07这块地址空间中读取数据出来。
整个过程,和数组有多大,并没有关系,计算机做的只是算出起始地址-->去该地址中取数据而已,因此我们看到使用普通for循环遍历ArrayList的速度很快,也很稳定。
LinkedList使用普通for循环遍历慢的原因
再看一下LinkedList的get方法做了什么:
public E get(int index) {
return entry(index).element;
}
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;
}
}
由于LinkedList是双向链表,因此第4行的意思是算出i在一半前还是一半后,一半前正序遍历、一半后倒序遍历,这样会快很多,当然,先不管这个,分析一下为什么使用普通for循环遍历LinkedList会这么慢。
原因就在第6~第7行,第11~第12行的两个for循里面,以前者为例:
1、get(0),直接拿到0位的Node0的地址,拿到Node0里面的数据
2、get(1),直接拿到0位的Node0的地址,从0位的Node0中找到下一个1位的Node1的地址,找到Node1,拿到Node1里面的数据
3、get(2),直接拿到0位的Node0的地址,从0位的Node0中找到下一个1位的Node1的地址,找到Node1,从1位的Node1中找到下一个2位的Node2的地址,找到Node2,拿到Node2里面的数据。
后面的以此类推。
也就是说,LinkedList在get任何一个位置的数据的时候,都会把前面的数据走一遍。假如我有10个数据,那么将要查询1+2+3+4+5+5+4+3+2+1=30次数据,相比ArrayList,却只需要查询10次数据就行了,随着LinkedList的容量越大,差距会越拉越大。其实使用LinkedList到底要查询多少次数据,大家应该已经很明白了,来算一下:按照前一半算应该是(1 + 0.5N) * 0.5N / 2,后一半算上即乘以2,应该是(1 + 0.5N) * 0.5N = 0.25N2 + 0.5N,忽略低阶项和首项系数,得出结论,LinikedList遍历的时间复杂度为O(N2),N为LinkedList的容量。
时间复杂度有以下经验规则:
O(1) < O(log2N) < O(n) < O(N * log2N) < O(N2) < O(N3) < 2N < 3N < N!
前四个比较好、中间两个一般、后3个很烂。也就是说O(N2)是相对糟糕的一种时间复杂度了,N大一点,程序就会执行得比较慢。
后记
切记一定不要使用普通for循环去遍历LinkedList。使用迭代器或者foreach循环(foreach循环的原理就是迭代器)去遍历LinkedList即可,这种方式是直接按照地址去找数据的,将会大大提升遍历LinkedList的效率。
以上这篇浅谈普通for循环遍历LinkedList弊端就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
# java
# linkedlist
# 遍历
# TypeScript数据结构链表结构 LinkedList教程及面试
# java集合Collection实现类解析ArrayList LinkedList及Vector
# java中LinkedList使用迭代器优化移除批量元素原理
# java中ArrayList和LinkedList的区别详解
# 浅析 ArrayList 和 LinkedList 有什么区别
# Java中集合LinkedList的原理与使用方法
# 一文讲透为什么遍历LinkedList要用增强型for循环
# 放在
# 就会
# 中找到
# 也就是说
# 给大家
# 越大
# 应该是
# 是一个
# 迭代
# 低阶
# 很好
# 就在
# 就像
# 是有
# 就能
# 一句
# 将会
# 最多
# 我有
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
微信小程序 scroll-view组件实现列表页实例代码
Laravel如何为API生成Swagger或OpenAPI文档
Laravel怎么定时执行任务_Laravel任务调度器Schedule配置与Cron设置【教程】
百度输入法ai面板怎么关 百度输入法ai面板隐藏技巧
Android中Textview和图片同行显示(文字超出用省略号,图片自动靠右边)
佛山网站制作系统,佛山企业变更地址网上办理步骤?
如何用免费手机建站系统零基础打造专业网站?
jimdo怎样用html5做选项卡_jimdo选项卡html5实现与切换效果【指南】
矢量图网站制作软件,用千图网的一张矢量图做公司app首页,该网站并未说明版权等问题,这样做算不算侵权?应该如何解决?
如何用已有域名快速搭建网站?
实现点击下箭头变上箭头来回切换的两种方法【推荐】
微信小程序 require机制详解及实例代码
JS中页面与页面之间超链接跳转中文乱码问题的解决办法
厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?
如何快速搭建高效WAP手机网站吸引移动用户?
Laravel如何处理文件上传_Laravel Storage门面实现文件存储与管理
Windows Hello人脸识别突然无法使用
如何在云虚拟主机上快速搭建个人网站?
ai格式如何转html_将AI设计稿转换为HTML页面流程【页面】
长沙企业网站制作哪家好,长沙水业集团官方网站?
青岛网站建设如何选择本地服务器?
浅谈Javascript中的Label语句
PythonWeb开发入门教程_Flask快速构建Web应用
Laravel如何发送系统通知?(Notification渠道示例)
Laravel如何使用Service Provider注册服务_Laravel服务提供者配置与加载
Laravel怎么实现前端Toast弹窗提示_Laravel Session闪存数据Flash传递给前端【方法】
bing浏览器学术搜索入口_bing学术文献检索地址
PHP正则匹配日期和时间(时间戳转换)的实例代码
Laravel如何使用withoutEvents方法临时禁用模型事件
Edge浏览器如何截图和滚动截图_微软Edge网页捕获功能使用教程【技巧】
Laravel怎么实现观察者模式Observer_Laravel模型事件监听与解耦开发【指南】
Laravel如何连接多个数据库_Laravel多数据库连接配置与切换教程
如何快速搭建高效香港服务器网站?
如何在橙子建站中快速调整背景颜色?
JavaScript实现Fly Bird小游戏
详解一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)
高防服务器:AI智能防御DDoS攻击与数据安全保障
如何自定义safari浏览器工具栏?个性化设置safari浏览器界面教程【技巧】
JavaScript如何实现错误处理_try...catch如何捕获异常?
Laravel事件和监听器如何实现_Laravel Events & Listeners解耦应用的实战教程
装修招标网站设计制作流程,装修招标流程?
如何在万网利用已有域名快速建站?
Laravel如何实现事件和监听器?(Event & Listener实战)
Laravel N+1查询问题如何解决_Eloquent预加载(Eager Loading)优化数据库查询
php增删改查怎么学_零基础入门php数据库操作必知基础【教程】
Laravel如何生成和使用数据填充?(Seeder和Factory示例)
如何解决hover在ie6中的兼容性问题
专业商城网站制作公司有哪些,pi商城官网是哪个?
如何用IIS7快速搭建并优化网站站点?
如何登录建站主机?访问步骤全解析

