通过 lambda 表达式创建多重映射

问题描述 投票:0回答:1

我正在读《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)))));
会做什么?也许,我很傻,但这对我来说确实很难。 我所理解的:

  1. 表达式
    Stream.of(values())
    创建并返回一个由
    enum Transition
    元素组成的数组
  2. 表达式
    t -> t.from
    仅表示
    enum Transition
    数组的每个元素,通过命令
    Stream.of(values())
    返回映射到
    Phase from
    元素。
  3. 表达式
    () -> new EnumMap<>(Phase.class)
    可能意味着要为每个
    Phase from
    元素创建地图。
  4. 我处于昏迷状态。
java lambda
1个回答
0
投票

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);
© www.soinside.com 2019 - 2024. All rights reserved.