Java 集合与映射的迭代,用户希望对集合或映射进行迭代,使用 java.lang.Iterable
或 java.util.Map
接口新增的默认方法 forEach
。除了使用循环对线性集合(即实现 Collection
或其后代的类)进行迭代外,可以通过 Iterable
接口引入的默认方法 forEach
实现同样的目的。
Java 集合与映射的迭代 问题描述
用户希望对集合或映射进行迭代。
Java 集合与映射的迭代 解决方案
使用 java.lang.Iterable
或 java.util.Map
接口新增的默认方法 forEach
。
Java 集合与映射的迭代 具体实例
除了使用循环对线性集合(即实现 Collection
或其后代的类)进行迭代外,可以通过 Iterable
接口引入的默认方法 forEach
实现同样的目的。
根据 Javadoc 的描述,forEach
方法的签名为:
default void forEach(Consumer<? super T> action)
forEach
方法的参数为 Consumer
,它是 java.util.function
包引入的一种函数式接口,表示传入一个泛型参数(generic argument)且不返回任何结果的操作。根据 Javadoc 的描述,“Consumer
与大部分函数式接口不同,它在执行时会产生副作用”。
纯函数(pure function)在执行时不会产生副作用。换言之,如果参数相同,则每次返回的结果也相同。函数式编程将其称为引用透明性(referential transparency),即函数可以被它的值所替换。
java.util.Collection
是 Iterable
的子接口,因此 forEach
方法适用于从 ArrayList
到 LinkedHashSet
的所有线性集合,这使得线性集合的迭代易如反掌(如例 5-17 所示)。
例 5-17 对线性集合进行迭代
List<Integer> integers = Arrays.asList(3, 1, 4, 1, 5, 9);
integers.forEach(new Consumer<Integer>() { ➊
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
integers.forEach((Integer n) -> { ➋
System.out.println(n);
});
integers.forEach(n -> System.out.println(n)); ➌
integers.forEach(System.out::println); ➍
}
❶ 匿名内部类实现
❷ 完整形式的 lambda 代码块
❸ lambda 表达式
❹ 方法引用
在本例中,匿名内部类只是显示为 Consumer
接口的 accept
方法的签名。观察内部类可知,accept
方法传入一个参数并返回 void
,本例所用的 lambda 表达式与之兼容。由于两个 lambda 表达式都包含对 System.out.println
方法的单次调用,forEach
方法可以用作方法引用。
Map
接口同样引入 forEach
方法作为默认方法,它传入 BiConsumer
:
default void forEach(BiConsumer<? super K, ? super V> action)
BiConsumer
也是 java.util.function
包新增的一种接口,表示一个传入两个泛型参数并返回 void
的函数。在实现 Map
接口的 forEach
方法时,参数是 entrySet
方法中 Map.Entry
实例的键和值。
换言之,对 Map
进行迭代现在就像对 List
、Set
或其他任何线性集合进行迭代一样简单。示例代码如例 5-18 所示。
例 5-18 对
Map
进行迭代
Map<Long, String> map = new HashMap<>();
map.put(86L, "Don Adams (Maxwell Smart)");
map.put(99L, "Barbara Feldon");
map.put(13L, "David Ketchum");
map.forEach((num, agent) ->
System.out.printf("Agent %d, played by %s%n", num, agent));
输出如例 5-196 所示。
6例 5-18 和例 5-19 取自美国经典电视剧《糊涂侦探》,这部由梅尔 • 布鲁克斯(Mel Brooks)和巴克 • 亨利(Buck Henry)编导的间谍喜剧在 1965 年到 1970 年间播出。主角麦克斯韦 • 史马特(Maxwell Smart)糅合了詹姆斯 • 邦德与雅克 • 克鲁索(英国电影《糊涂大侦探》主角)的特点,又称特工 86 号(Agent 86)。他的女搭档是特工 99 号(Agent 99),同事是特工 13 号(Agent 13)。
例 5-19 对
Map
迭代后的输出
Agent 99, played by Barbara Feldon
Agent 86, played by Don Adams (Maxwell Smart)
Agent 13, played by David Ketchum
在 Java 8 之前,为了实现对 Map
的迭代,需要首先通过 keySet
方法获取键的 Set
,或通过 entrySet
方法获取 Map.Entry
实例。而 Java 8 引入的默认方法 forEach
使迭代操作得以简化。
跳出 for-each 循环并非易事,这一点请谨记在心。为解决这个问题,考虑将流处理代码重写为
filter
或sorted
,并后跟findFirst
。