整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

java遍历List的三种方式比较 用数据说明为什么

java遍历List的三种方式比较 用数据说明为什么推荐使用foreach

考链接:

http://www.trinea.cn/android/arraylist-linkedlist-loop-performance/

https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.14.2

foreach也称为增强for循环,是java5新特性,可用于遍历数组或实现了Iterable接口的集合容器。

1.遍历List的方式:

假设已有数据:

List<Integer> list;

(1) foreach循环:

for (Integer j : list) {

// use j

}

(2) 下标递增(递减)循环:

int size=list.size();

for (int j=0; j < size; j++) {

list.get(j);

}

(3) 迭代器循环迭代:

for (Iterator<Integer> iterator=list.iterator(); iterator.hasNext();) {

iterator.next();

}

2.三种遍历方式的性能比对

经测试通过上述三种方式分别迭代ArrayList和LinkedList时所消耗的时间如下:

compare loop performance of ArrayList

-----------------------------------------------------------------------

list size | 10,000 | 100,000 | 1,000,000 | 10,000,000

-----------------------------------------------------------------------

for each | 1 ms | 3 ms | 14 ms | 152 ms

-----------------------------------------------------------------------

for iterator | 0 ms | 1 ms | 12 ms | 114 ms

-----------------------------------------------------------------------

for size=list.size() | 0 ms | 0 ms | 6 ms | 62 ms

-----------------------------------------------------------------------

compare loop performance of LinkedList

-----------------------------------------------------------------------

list size | 100 | 1,000 | 10,000 | 100,000

-----------------------------------------------------------------------

for each | 0 ms | 1 ms | 1 ms | 2 ms

-----------------------------------------------------------------------

for iterator | 0 ms | 0 ms | 0 ms | 2 ms

-----------------------------------------------------------------------

for size=list.size() | 0 ms | 0 ms | 67 ms | 8216 ms

3.List遍历方式及其性能表现分析:

ArrayList:

当size < 100万时,三种方式性能差别不大;

当size >=100万时,for > iterator >=foreach.

LinkedList:

当size小于1万时,三种方式性能差别不大;

当size>=1万时, iterator >=foreach > for.

由于foreach底层也是通过iterator来迭代,因此foreach的性能与iterator接近。

是什么影响了这三种遍历方式的性能呢?

主要原因是List的底层数据结构和从List容器中获取元素的算法。

我们知道ArrayList的底层数据结构是数组,LinkedList是链表。

这三种遍历方式从List中获取元素的算法分别为:

1) foreach循环和iterator迭代器:

都是调用iterator.next(),查看ArrayList对于iterator中next方法的实现可知其最终是通过数组下标获取元素。如下图:

ArrayList迭代之next

查看LinkedList对于iterator中next方法的实现可知其最终是调用了父类AbstractList中iterator的实现,然后调用了get(index),而LinkedList的get方法是通过遍历链表来获取元素的。如下图:

2) for循环:调用get(index).

综上,这三种遍历方式从容器中获取元素的方式如下图:

可见遍历ArrayList时最终都是直接通过数组下标定位元素的,这应该是当ArrayList的容量在100万以内时,其遍历时间在20ms以内的根本原因。

遍历LinkedList时最终都是查询链表。但为什么iterator方式遍历LinkedList的速度是for的数千倍呢?因为iterator利用了游标,而后者是调用了get(index)方法将链表从头到尾或从尾到头查了一遍。

4.为什么推荐使用foreach

最后我们来看一下为什么诸如《Effective-Java》都推荐使用foreach:

1) 性能较优:

分析上文性能测试结果发现,通过foreach遍历ArrayList时当size < 100万时,时间在20ms以内,并不比for循环差多少。遍历size在百万以上的超大ArrayList的时其性能明显低于for循环遍历方式。

通过foreach遍历LinkedList时,无论size大小,其性能始终接近于最优的iterator遍历方式。

2) 书写简洁。

3) 不必关心元素下标,可避免数组下标越界。

最后别忘了点波关注哦!

JavaScript作为Web前端开发的基石,其强大的功能和灵活性不仅体现在网页的动态交互上,更在于其处理数据的能力。数组遍历是JavaScript中最常见的操作之一,尤其在算法题的求解过程中,它扮演着至关重要的角色。本文将深入探讨JavaScript中数组遍历的多种方法,通过具体的算法题示例,帮助读者掌握高效解决问题的技巧。


技术概述

数组遍历方法

在JavaScript中,数组遍历可以通过多种方式进行,每种方法都有其特点和适用场景:

  • for循环:最传统的遍历方式,适用于所有情况。
  • forEach():ES5引入的数组方法,简化了遍历语法。
  • map():用于创建新数组,对原数组的每个元素进行映射操作。
  • filter():用于筛选数组,返回满足条件的元素组成的新数组。
  • reduce():用于对数组元素进行累积操作,常用于求和、合并等场景。
  • some()every():用于检查数组中是否存在满足条件的元素或所有元素是否都满足条件。

代码示例

const numbers=[1, 2, 3, 4, 5];

// 使用for循环遍历
for (let i=0; i < numbers.length; i++) {
    console.log(numbers[i]);
}

// 使用forEach遍历
numbers.forEach(number=> console.log(number));

// 使用map创建新数组
const doubled=numbers.map(number=> number * 2);
console.log(doubled); // 输出: [2, 4, 6, 8, 10]

技术细节

工作原理

数组遍历方法本质上是通过迭代数组中的每一个元素来执行特定的逻辑操作。不同的方法提供不同的操作能力,如map用于变换,filter用于筛选,而reduce用于聚合。

难点分析

  • 性能考量:尽管现代JavaScript引擎进行了大量的优化,但在处理大规模数据时,遍历方法的选择仍然会影响性能。
  • 副作用管理:在遍历时避免对原始数组造成不必要的修改,尤其是使用mapfilter时。

实战应用

应用场景

假设我们有一道算法题,要求找出数组中所有偶数,并返回它们的平方和。

代码示例

function sumOfSquaresEvenNumbers(numbers) {
    return numbers
        .filter(number=> number % 2===0) // 筛选偶数
        .map(number=> number * number)     // 平方
        .reduce((acc, curr)=> acc + curr, 0); // 求和
}

const result=sumOfSquaresEvenNumbers([1, 2, 3, 4, 5, 6]);
console.log(result); // 输出: 56

优化与改进

潜在问题

  • 性能瓶颈:对于大数据集,多次迭代可能会导致性能下降。
  • 代码冗余:过度使用高阶函数可能导致代码不易理解。

代码示例

function optimizedSumOfSquaresEvenNumbers(numbers) {
    let sum=0;
    for (let number of numbers) {
        if (number % 2===0) {
            sum +=number * number;
        }
    }
    return sum;
}

const optimizedResult=optimizedSumOfSquaresEvenNumbers([1, 2, 3, 4, 5, 6]);
console.log(optimizedResult); // 输出: 56

常见问题

  • Q: 如何在遍历数组时避免修改原数组?
  • A: 使用mapfilter等方法,它们会返回新数组,而不会修改原数组。

总结与展望

数组遍历不仅是JavaScript编程的基础,也是解决复杂算法问题的利器。通过本文的探讨,我们不仅学习了多种数组遍历的方法,还掌握了如何在实际问题中选择合适的遍历策略,以提高代码的效率和可读性。未来,随着JavaScript语言的不断发展,新的数组方法和迭代器模式将进一步丰富我们的编程工具箱,为开发者提供更加高效和灵活的解决方案。掌握数组遍历的技巧,意味着在算法题的求解中拥有了更多的选择和自信,这也是前端开发者迈向更高层次的关键一步。

ava中的foreach循环的用法