我正在读《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);
}
}
}
但是我无法理解有助于创建这样一个多重映射的 lambda 表达式详细信息:我的意思是命令
toMap(t -> t.to, t -> t, (x, y) -> y, () -> new EnumMap<>(Phase.class)))));
会做什么?也许,我很傻,但这对我来说确实很难。
我所理解的:
Stream.of(values())
创建并返回一个由 enum Transition
元素组成的数组t -> t.from
仅表示enum Transition
数组的每个元素,通过命令Stream.of(values())
返回映射到Phase from
元素。() -> new EnumMap<>(Phase.class)
可能意味着要为每个Phase from
元素创建地图。toMap
是“下游收集器”。它被用来收集外部地图的values。换句话说,它创建并填充内部映射。
groupingBy
|
|--------------------------------|
| |
Map<Phase, Map<Phase, Transition>>
| |
|--------------------|
|
toMap
groupingBy
收集器用于根据某个键对多个元素进行“分组”。由于多个值可以映射到一个键,groupingBy
需要知道如何收集这些值(“默认”方式是将值收集到列表中;这里我们将它们收集到映射中)。
groupingBy(
// The "key extractor". What we want to group the elements by.
t -> t.from,
// The "map supplier". Creates the map used to group elements.
() -> new EnumMap<>(Phase.class),
// The "downstream collector". Collects the multiple values associated with each key.
toMap(...)
)
如上所述,
toMap
收集器负责收集外部地图的值。
toMap(
// The "key extractor". What we want the map's keys to be.
t -> t.to,
// The "value extractor". What we want the map's values to be (the identity
// function is used here, meaning the element itself is the value).
t -> t,
// The "merge function". Merges two values mapped to the same key (this simply
// returns the second argument, essentially replacing the previous value).
(x, y) -> y,
// The "map supplier". Creates the map to put the entries into.
() -> new EnumMap<>(Phase.class)
)
以更“传统”的形式看待这一点也可能有所帮助。
// groupingBy: () -> new EnumMap<>(Phase.class)
Map<Phase, Map<Phase, Transition>> outerMap = new EnumMap<>(Phase.class);
// the stream
for (Transition t : Transition.values()) {
Phase outerKey = t.from; // groupingBy: t -> t.from
// groupingBy: toMap(...)
Map<Phase, Transition> innerMap = outerMap.get(outerKey);
if (innerMap == null) {
// toMap: () -> new EnumMap<>(Phase.class)
innerMap = new EnumMap<>(Phase.class);
outerMap.put(outerKey, innerMap);
}
Phase innerKey = t.to; // toMap: t -> t.to
Transition innerValue = t; // toMap: t -> t
// toMap: (x, y) -> y
innerMap.merge(innerKey, innerValue, (oldVal, newVal) -> newVal);
}
请注意,由于合并函数只是返回第二个值,这与替换当前值相同,因此在这种情况下,上面的
innerMap.merge
可以替换为:
innerMap.put(innerKey, innerValue);