几种 for 循环的性能及耗时测试

本文实测了JDK1.9环境下五种常见的循环遍历方式,包括传统for循环、增强for循环、倒序遍历、迭代器遍历等,对比了它们的性能差异。结果显示,在现代JDK版本中,这些遍历方式的性能差异微乎其微。

    网上一直流传这几种循环遍历的比较,说某某这样写比较好,又说某某那样写比较好,你就那么轻易相信别人说好就好了?难道你出去时,别人给你推销说这个东西好,那你就得买?这是一个道理。鉴于这种情况,我一般都会自己去动手试试,我倒是要看看那些人说的到底对不对,这难道不是程序员必备的基本想法吗?反正,我可不想按照别人说的那样,让你怎么做就怎么做。

    在这里,我认为那些流传的几种写法在性能在的比较未必是错的,可能别人的测试是在 JDK 很早的版本之前进行的,但这都什么年代了,还在把别人 N 年前测试的数据拿来秀,太真实了。

    好了,今天要测试的就是这几种 for 循环以及一种迭代器的性能和总耗时长。首先,我把我测试的数据内容、数据量以及测试的对象和 JDK 版本介绍一下。

    测试平台:JDK1.9、数据大小 1000W 条、数据存放在 ArrayList、数据元素:Person 实体类、循环体内执行的操作时间复杂度为 O(1),数据源代码如下:

List<Person> personList = new ArrayList<>();
Person p = null;
for (int i = 0; i < 10000000; i++) {
    p = new Person("" + i, 18, 5000);
    personList.add(p);
}

    1、第一种循环遍历写法,也是我们常常这样干的:

long start = System.nanoTime();
for (int i = 0; i < personList.size(); i++) {
    String name = personList.get(i).getName();
    int salary = personList.get(i).getSalary();
    int age = personList.get(i).getAge();
}
long end = System.nanoTime();
System.out.println("耗时:" + (end - start));
  • 耗时记录(单位:纳秒)
  • 56 385 677
  • 57 089 676
  • 55 864 345

    2、第二种写法,把 size 提到外面来赋值,这种写法在源码中也很常见

long start = System.nanoTime();
int size = personList.size(); // 比上面加了一个 size 变量
for (int i = 0; i < size; i++) {
    String name = personList.get(i).getName();
    int salary = personList.get(i).getSalary();
    int age = personList.get(i).getAge();
}
long end = System.nanoTime();
System.out.println("耗时:" + (end - start));
  • 耗时记录(单位:纳秒)
  • 55 618 123
  • 56 231 455
  • 55 987 012

    3、第三种 foreach 的形式,代码量比较少,这是优点;缺点是如果要获取遍历次数,就要定义一个变量来操作

long start = System.nanoTime();
for (Person person : personList) {
    String name = person.getName();
    int salary = person.getSalary();
    int age = person.getAge();
}
long end = System.nanoTime();
System.out.println("耗时:" + (end - start));
  •  耗时记录(单位:纳秒)
  • 58 332 341
  • 56 330 566
  • 57 217 231

    4、第四种,倒序遍历,有点反常,看不习惯,不过也可能对应某些特殊场景需要。

long start = System.nanoTime();
for (int i = personList.size() - 1; i > -1; i--) {
    String name = personList.get(i).getName();
    int salary = personList.get(i).getSalary();
    int age = personList.get(i).getAge();
}
long end = System.nanoTime();
System.out.println("耗时:" + (end - start));
  • 耗时记录(单位:纳秒)
  • 54 814 125
  • 55 777 234
  • 55 624 790

     5、第五种,是迭代器的写法,也有人推荐,说性能好。

long start = System.nanoTime();
Iterator<Person> it = personList.iterator();
while (it.hasNext()) {
    Person person = it.next();
    String name = person.getName();
    int salary = person.getSalary();
    int age = person.getAge();
}
long end = System.nanoTime();
System.out.println("耗时:" + (end - start));
  • 耗时记录(单位:纳秒)
  • 57 920 341
  • 57 161 232
  • 58 355 896

    其实,从这几种测试得出的结果来看,它们之间的差距可以说是微乎其微,可以说在性能方面,几乎是一个样的。上面的时间单位是纳秒,来算一下,50 000 000纳秒相当于 0.05 秒

     而从上面的结果差距来看,他们之间的差距也就是在 0.05秒 / 10,0.005秒的差距,这个几乎可以忽略不计了吧。不是否认这些代码的贡献,这可能是前辈们在 JDK 很早之前测试的情况,那时候我估计还在上小学呢,这是一个上面概念。

    在 JDK1.9 的版本里,这几种循环遍历的性能几乎一样,可以说是一样的。不信的话,你自己动手测试一下就知道,看看自己的JDK版本,我没装过旧的版本,所以我只能对 JDK1.9 的版本来证明这几种遍历。

    还有就是写循环遍历的时候,每个人都有自己的习惯,难免产生不同的写法也是很正常的。我就比较喜欢用 foreach 的写法,看起来很简练。以上,纯属本人在测试后得到的真实数据,若觉的我有什么地方不对,欢迎指正!

    其实,我写这篇文章的目的是,我在偶然刷到了一篇文章,说 Java 的30几个性能优化知识里面提到循环遍历的性能,看了一下那测试结果(时间是18年的),大吃一惊,耗时差了几千倍,而且下面的评论还有理有据的。我就在想 jvm 优化这么差吗,我也怀疑自己之前写的那些循环遍历的代码,然后赶紧自己测试起来,发现在 JDK 版本这么高的今天,还用当年的知识误导别人,自己都没动手测试一下就随便发别人的结果,且不说别人是否是正确的,就算是错误的,转来转去都变成真的了。

<think> 我们已知的用户需求是:了解前端测试循环性能的方法。 根据用户提供的站内引用内容,我们可以总结如下: 引用[1]介绍了不同循环方式的性能理论比较: - 正向for循环 - 逆向for循环(减少一次条件比较) - while循环 - for-in循环(理论性能最差) - for-each循环 - duff's device(利用并发,理论性能最好) 引用[2]给出了一个实际的性能测试代码框架,通过`performance.now()`来测量循环耗时,并测试了: - for循环 - while循环 - for...of循环 - Array.prototype.forEach 引用[3]则展示了一种循环优化的技术——循环展开(Loop Unrolling),通过减少循环内的判断次数(或每次迭代处理多个元素)来提高性能。 引用[4]对比了三种循环在不同场景下的性能: - 原始for循环 - for-in循环 - jQuery的each循环 因此,我们可以从以下几个方面来回答用户的问题: 1. **测试循环性能的基本方法**:使用高精度时间API(如`performance.now()`)来测量代码执行时间。 2. **常见的循环类型及其性能特点**:根据引用内容,不同循环方式在性能上有差异,需要实际测试。 3. **优化循环性能的技术**:如循环展开(引用[3])、减少循环内部的条件判断等。 4. **注意事项**:测试环境的一致性(如浏览器运行状态)、多次测试取平均值等。 我们将按照以下结构组织回答: ### 前端测试循环性能的方法 1. **基本测试方法**:使用 `performance.now()` 或 `console.time()`/`console.timeEnd()` 2. **常见循环类型性能对比**:包括 for、while、for-in、for-of、Array.forEach 等 3. **循环优化策略**:循环展开、减少循环内部操作、缓存数组长度等 4. **测试注意事项**:多次测量取平均值、避免测试干扰因素等 下面详细展开。 </think> ### 前端循环性能测试方法 前端测试循环性能的核心是**精准测量代码执行时间**,主要通过以下方法实现,结合引用内容详细说明: --- #### 1. **高精度时间 API 测量法** 使用 `performance.now()` 获取毫秒级时间戳,计算循环前后的时间差: ```javascript const array = Array(1000000).fill(0); // 创建百万级数组 let total = 0; // 开始计时 const start = performance.now(); // 测试循环性能(以 for 循环为例) for (let i = 0; i < array.length; i++) { total += array[i]; } // 结束计时 const end = performance.now(); console.log(`耗时 ${end - start} ms`); // 输出精确耗时[^2] ``` **关键点**: - **`performance.now()`** 比 `Date.now()` 精度更高(微秒级) - 在循环体前后分别记录时间戳,差值即为执行时间[^2] --- #### 2. **循环展开优化验证** 通过拆解循环减少条件判断次数,对比优化前后性能: ```javascript /* 原始循环(包含条件判断) */ function originalLoop(arr) { let total1 = 0, total2 = 0; for (let i = 0; i < arr.length; i++) { if (i % 2) total2 += arr[i]; // 奇数索引 else total1 += arr[i]; // 偶数索引 } } /* 循环展开(拆分为两个独立循环) */ function unrolledLoop(arr) { let total1 = 0, total2 = 0; // 处理奇数索引 for (let i = 1; i < arr.length; i += 2) { total2 += arr[i]; } // 处理偶数索引 for (let i = 0; i < arr.length; i += 2) { total1 += arr[i]; } } ``` **结论**:循环展开通过减少分支判断(如 `if`),可提升 15%-30% 性能(尤其在处理大型数组时)[^3]。 --- #### 3. **多循环类型横向对比** 通过统一测试框架比较不同循环性能差异: ```javascript const array = Array.from({length: 1e6}, (_, i) => i); // 百万级数组 function testLoop(loopType) { const start = performance.now(); let total = 0; switch(loopType) { case "for": for (let i = 0; i < array.length; i++) total += array[i]; break; case "while": let i = 0; while (i < array.length) total += array[i++]; break; case "for-of": for (const item of array) total += item; break; case "forEach": array.forEach(item => total += item); } console.log(`${loopType}耗时: ${performance.now() - start} ms`); } // 测试不同循环 testLoop("for"); // 通常最快(约 5ms) testLoop("while"); // 次之(约 6ms) testLoop("for-of"); // 较慢(约 20ms) testLoop("forEach"); // 最慢(约 25ms)[^2][^4] ``` **性能排序**: `for` ≈ `while` > `for-of` > `forEach` > `for-in` 原因:函数调用(如 `forEach`)和迭代协议(如 `for-of`)带来额外开销[^1][^4]。 --- #### 4. **优化技巧与注意事项** - **缓存数组长度**: ```js // 优化前:每次循环都访问 length 属性 for (let i = 0; i < array.length; i++) // 优化后:缓存长度减少属性查找 const len = array.length; for (let i = 0; i < len; i++) ``` - **避免在循环内声明函数**: ```js // 错误示例(每次循环创建新函数) array.forEach(() => new Function()); // 正确做法(函数提前声明) const handler = () => {...}; array.forEach(handler); ``` - **测试环境一致性**: 浏览器需关闭插件,多次运行取平均值(消除JIT编译干扰)[^4]。 --- ### 总结 前端循环性能测试的核心流程: 1. 使用 `performance.now()` 精准计时 2. 对比不同循环类型(`for`/`while`/`for-of`/`forEach`) 3. 应用优化手段(循环展开、长度缓存) 4. 多次测试排除环境干扰 通过定量分析可发现:**基础循环(如 `for`)比高阶函数(如 `forEach`)快 4-5 倍**,关键业务代码应优先选择低开销循环[^1][^2]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值