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。
极客教程