关于《Effective Java》中Lambda创建Multimap的代码逻辑疑问
先看这段来自《Effective Java》的枚举代码:
public enum Phase { SOLID, LIQUID, GAS; public enum Transition { MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID), BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID), SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID); private final Phase from; private final Phase to; Transition(Phase from, Phase to) { this.from = from; this.to = to; } private static final Map<Phase, Map<Phase, Transition>> m = Stream.of(values()) .collect(groupingBy(t -> t.from, () -> new EnumMap<>(Phase.class), toMap(t -> t.to, t -> t, (x, y) -> y, () -> new EnumMap<>(Phase.class)))); public static Transition from(Phase from, Phase to) { return m.get(from).get(to); } } }
你已经理清的部分逻辑:
Stream.of(values())生成Transition枚举所有元素的流t -> t.from把每个Transition元素按起始Phase分组() -> new EnumMap<>(Phase.class)为每个分组创建EnumMap作为外层容器
下面重点拆解toMap(t -> t.to, t -> t, (x, y) -> y, () -> new EnumMap<>(Phase.class))四个参数的作用:
第一个参数:t -> t.to
这是键映射函数,用来指定内层Map的key。对每个Transition元素,我们把它的to属性(目标Phase)作为内层Map的键,方便后续通过目标Phase快速定位对应的状态转换。
第二个参数:t -> t
这是值映射函数,用来指定内层Map的value。这里直接把Transition元素本身作为值,也就是内层Map存的是「目标Phase -> 对应Transition枚举」的映射关系。
第三个参数:(x, y) -> y
这是合并函数,用来处理键冲突场景。如果两个Transition元素对应同一个from和to(即同一组起始+目标Phase),这个函数决定保留哪个值。这里逻辑是保留后出现的元素(y),不过在这段代码里不会出现这种冲突——所有状态转换都是唯一的,这个参数更多是满足toMap重载方法的参数要求,同时给代码留了容错空间。
第四个参数:() -> new EnumMap<>(Phase.class)
这是Map供应函数,指定内层Map的具体实现类型。这里选择EnumMap是因为它是专门为枚举设计的Map,性能比HashMap更优,且键的类型固定为Phase枚举,类型更安全。
整体来说,这四个参数配合,把每个分组内的Transition元素转换成「目标Phase -> Transition枚举」的EnumMap,最终和外层分组逻辑一起,构建出起始Phase -> (目标Phase -> 转换枚举)的双层映射结构,方便快速通过起始和目标Phase找到对应的状态转换。
内容的提问来源于stack exchange,提问作者Александр Скворцов

