返回 导航

其他

hangge.com

Java - 集合遍历的7种方式详解(for、foreach、iterator、并行流等)

作者:hangge | 2023-02-10 08:52
    日常开发中常常需要对集合中的对象进行遍历,Java 中遍历集合的方式有许多种,如:基本的 for 循环、迭代器、foreach 循环等等,下面通过样例分别进行演示。

1,使用基本的 for 循环

这也是最简单,最基础的遍历方式。不过该方式需要知道集合的长度,不适合所有集合。
List<String> list = Arrays.asList("hangge", "google", "baidu");
for (int i = 0; i < list.size(); i++) {
  System.out.println(list.get(i));
}

2,使用迭代器遍历

(1)大多数的集合类(例如 java.util.List、java.util.Setjava.util.Map)都提供了 iterator() 方法来返回一个 java.util.Iterator 对象,用于遍历集合中的元素。下面是一个简单的样例:
注意:虽然 java.util.Enumeration 也可以用来遍历集合中的元素。不过,java.util.Enumeration 接口在 Java 的新版本中已经被认为是过时的,应该使用 java.util.Iterator 接口代替。
List<String> list = Arrays.asList("hangge", "google", "baidu");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
  String element = iterator.next();
  System.out.println(element);
}

(2)与下面的 for-each 循环相比,使用 Iterator 的方式更加灵活,因为它允许手动控制迭代过程,例如在迭代过程中修改集合、跳过元素或在多个集合之间进行迭代。比如下面样例在迭代过程中修改集合:
注意:使用 for-each 循环时不能在循环内修改集合,否则会抛出 java.lang.UnsupportedOperationException 异常。
List<String> list = Arrays.asList("hangge", "google", "baidu");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
  String s = iterator.next();
  if (s.equals("google")) {
    iterator.remove();
  }
}

3,使用 for-each 循环(也称为增强型 for 循环)

(1)for-each 循环遍历使用 Iterator 的方式在语法上略有不同,但基本上是等价的。下面是一个使用样例:
List<String> list = Arrays.asList("hangge", "google", "baidu");
for (String s : list) {
  System.out.println(s);
}

(2)for-each 循环本质上是使用了迭代器模式,它将迭代器的实现细节隐藏在了语法层面。当使用 for-each 循环遍历集合时,编译器会将其转换为使用迭代器的方式。比如上面代码会被编译器转换为类似于以下代码,在底层实现上,for-each 循环和使用 Iterator 的方式是等价的:
List<String> list = Arrays.asList("hangge", "google", "baidu");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
  String s = iterator.next();
  System.out.println(s);
}

4,使用 Java 8 的 forEach 方法遍历

(1)从 Java 8 开始,我们可以使用 forEach() 方法来迭代列表的元素,这个方法在 Iterable 接口中定义。下面是一个简单样例:
注意:虽然 forEach 方法可以很方便地遍历任何实现了 Iterable 接口的集合(它本身就是基于 Iterator 实现的),但是它并不能用来遍历数组。
List<String> list = Arrays.asList("hangge", "google", "baidu");
list.forEach(element -> {
  System.out.println(element);
});

(2)该方式还可以配合方法引用来使用,下面代码效果通上面一样:
List<String> list = Arrays.asList("hangge", "google", "baidu");
list.forEach(System.out::println);

5,使用 Stream API 的 forEach 方法遍历

(1)使用 Stream API 可以很方便地对集合进行各种操作,下面是一个简单的样例:
List<String> list = Arrays.asList("hangge", "google", "baidu");
list.stream().forEach(element -> {
  System.out.println(element);
});

(2)该方式同样可以配合方法引用来使用,下面代码效果通上面一样:
List<String> list = Arrays.asList("hangge", "google", "baidu");
list.stream().forEach(System.out::println);

(3)Stream APIforEach 方法出了可以遍历集合的,还可以用来遍历任何支持流的对象,包括集合、数组、文件、函数生成器等。
String[] array = {"hangge", "google", "baidu"};
Arrays.stream(array).forEach(element -> {
  System.out.println(element);
});

6,使用 ListIterator 接口遍历集合

(1)ListIteratorIterator 接口的子接口。ListIterator 可以向前或向后遍历列表中的元素,并允许在列表中插入和替换元素。下面是一个简单的样例,可以看到向后遍历和 Iterator 用法是一样的:
List<String> list = Arrays.asList("hangge", "google", "baidu");
ListIterator<String> iterator = list.listIterator();
while (iterator.hasNext()) {
  String name = iterator.next();
  System.out.println(name);
}

(2)我们还可以使用 ListIterator 向前遍历 List 中的元素:
注意:要向前遍历的话需要创建了一个从列表末尾开始的 ListIterator
List<String> list = Arrays.asList("hangge", "google", "baidu");
//创建了一个从列表末尾开始的ListIterator
ListIterator<String> iterator = list.listIterator(list.size());
while (iterator.hasPrevious()) {
  String name = iterator.previous();
  System.out.println(name);
}

(3)我们还可以使用 ListIterator 来修改、添加或删除集合中的元素,比如下面样例我们将 hangge 修改为 hangge.com,在 baidu 之后添加了一个新元素 apple,并删除了所有其他元素。
注意:为了能够添加删除元素,这里我们使用的是 ArrayList(或其他可变列表类型)。因为 Arrays.asList() 方法创建的列表的大小是固定的,因此我们不能使用 addremove 方法更改列表的大小。否则将抛出 UnsupportedOperationException 异常。
//创建一个可变列表类型
List<String> list = new ArrayList<>(Arrays.asList("hangge", "google", "baidu"));
ListIterator<String> iterator = list.listIterator();
while (iterator.hasNext()) {
  String name = iterator.next();
  if (name.equals("hangge")) {
    iterator.set("hangge.com"); // 修改元素
  } else if (name.equals("baidu")) {
    iterator.add("apple"); // 在 baidu 之后添加元素
  } else {
    iterator.remove(); // 删除元素
  }
}
System.out.println(list);

7,使用并行流遍历

(1)并行流使用多个线程来并行处理集合中的元素,可以提高处理速度。上面的代码使用了并行流来遍历集合 list 中的元素,并将它们打印出来。
(1)具体有多少个线程同时遍历列表取决于你的系统和 JVM 的设置。Java 8 中的并行流使用了 Fork/Join 框架来实现多线程并行处理。Fork/Join 框架会自动根据系统的资源情况决定使用多少个线程来执行任务。
(2)我们可以使用 ForkJoinPool.commonPool() 方法获取系统的公共 ForkJoin 池,并使用 getParallelism 方法获取池中的线程数。下面可以查看有多少个线程可用来执行并行流的任务了。
  • int parallelism = ForkJoinPool.commonPool().getParallelism();
(3)我们可以通过指定系统属性 java.util.concurrent.ForkJoinPool.common.parallelism 来控制系统的公共 ForkJoin 池中线程的数量。例如,你可以在启动 JVM 时指定这个系统属性,比如下面命令会启动 4 个线程来执行并行流的任务:
  • java -Djava.util.concurrent.ForkJoinPool.common.parallelism=4 Hangge
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
// 创建一个并行流(将parallel标志指定为true表示创建并行流)
Stream<Integer> parallelStream = list.parallelStream();
// 查看流是否支持并行遍历
System.out.println("流是否支持并行遍历: " + parallelStream.isParallel());
// 使用 forEach() 方法遍历并行流
parallelStream.forEach(element -> {
  System.out.println(element);
});

(2)运行结果如下,由于使用了多个线程同时遍历列表中的元素,输出结果可能是乱序的。
注意:使用并行流时,你需要注意线程安全问题。如果你的代码中存在竞争条件或共享可变数据,那么可能会导致程序出现错误。因此,使用并行流时,应该避免共享可变数据,或者使用同步机制来保证线程安全。

附:各种遍历方式的使用场景

1,使用 for 循环

    在性能要求不高的情况下,使用普通的 for 循环遍历集合是一种简单方便的选择。我们也可以使用增强型的 for 循环(也称为 "for-each" 循环)来遍历集合,但这种方式通常比普通的 for 循环慢一些。

2,使用迭代器

    当我们需要在遍历集合的同时对其进行修改时,使用迭代器是一个不错的选择。迭代器可以让我们在遍历集合的同时删除或添加元素,而不需要了解底层实现细节。

3,使用 ListIterator

    如果我们需要在遍历 List 集合的同时进行修改,并且希望能够在遍历过程中向前或向后移动遍历位置,那么使用 ListIterator 就是一个不错的选择。

4,使用并行流

    如果我们需要对大型集合进行并行处理,那么使用并行流就是一种不错的选择。并行流使用多个线程来并行处理集合中的元素,可以提高处理速度。

5,使用 forEach 方法

    Java 8 中引入了 forEach 方法,它允许我们使用 Lambda 表达式来遍历集合。这是一种简单方便的遍历方式,并且在性能方面也很优秀。
评论

全部评论(0)

回到顶部