嵌套的EnumMap使用流(Streams)

huangapple go评论76阅读模式
英文:

Nested EnumMap using Streams

问题

我开始学习Java流,并在一本名为"effective programming"的书的讲座中找到了这段代码:

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);
        }
    }
}

我无法理解这个EnumMap是如何创建的。对我来说最难理解的部分是:

toMap(t -> t.to, t -> t, (x, y) -> y, () -> new EnumMap<>(Phase.class))));

有人能解释给我吗?

英文:

I'm starting with Java streams and during the lecture of effective programming book i found this piece of code:

    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&lt;Phase, Map&lt;Phase, Transition&gt;&gt;
    	m = Stream.of(values()).collect(groupingBy(t -&gt; t.from,
    		() -&gt; new EnumMap&lt;&gt;(Phase.class),
    		toMap(t -&gt; t.to, t -&gt; t,
    			(x, y) -&gt; y, () -&gt; new EnumMap&lt;&gt;(Phase.class))));
    
    public static Transition from(Phase from, Phase to) {
    	return m.get(from).get(to);
    }
  }
}

And I cannot understand how this EnumMap is created. The most difficult to understand for me is :

toMap(t -&gt; t.to, t -&gt; t,
                (x, y) -&gt; y, () -&gt; new EnumMap&lt;&gt;(Phase.class))));

Does anybody could explain it to me ?

答案1

得分: 1

以下是翻译好的内容:

代码的功能:
你有一系列的元素,希望按照初始阶段对它们进行分组。在这里,作者选择使用复杂的分组操作,这个操作还将执行分组数据的后转换。这就是 toMap 函数的作用。一旦 groupingBy 收集器开始缓冲元素,它会委托给 mapTo 收集器为每个组构建自定义记录。在这里,作者使用 toMap 来通过它们的 目标 状态(to)对各个阶段进行索引。

所以,让我们进行分解:

Stream.of(values())
   // 查询结果
   .collect(
       // 元素必须使用从每个流值中提取的键进行分组
       groupingBy(
           // 指定如何提取分组键
           t -> t.from,
           // 在这里,作者自己指定如何创建分组映射,
           // 以确保它将使用自定义的枚举优化的键管理。
           () -> new EnumMap<>(Phase.class),
           // 嵌套另一个收集器,将每个组的值列表转换为其他内容。
           // 注意,toMap 不会为键缓冲值的集合。
           // 它设计用于将键映射到每个值。
           // 但是,它允许用户在需要时合并冲突的值。
           toMap(
               // 与 groupby 相同,我们指定如何从值中提取键。
               t -> t.to,
               // 在这里,指定与键相关联的值是我们从中提取键的对象。
               // 在这里,我们可以将其更改为其他内容(例如文本表示)。
               t -> t,
               // toMap 将一个键绑定到一个值。我们必须自己解决冲突。
               // 在这里,作者决定任意丢弃一个值。
               (x, y) -> y,
               // 与 groupby 操作一样,作者强制使用专门用于枚举键的映射实现。
               () -> new EnumMap<>(Phase.class))));

最后,官方的流集合文档提供了相当多的解释。

我不确定我的解释是否足够,您可以在评论中提供备注。

英文:

What the code do:
You have a stream of elements, and want to group them by initial phase. Here the author chose to use a complex group by operation, which will also perform post-transformation of groupped data. This is where toMap function comes in. Once groupingBy collector starts buffering elements, it delegates to mapTo collector to build custom records for each group. Here, the author uses toMap to index phases by their target state: to.

So, let's decompose it :

Stream.of(values())
   // Queries a result
   .collect(
       // Elements must be grouped using a key, extracted from each streamed value
       groupingBy(
           // Specify how to extract the grouping key
           t -&gt; t.from,
            // Here, the author specifies himself how to create the group map,
            // to be sure it will use a custom enum-optimized key management.
            () -&gt; new EnumMap&lt;&gt;(Phase.class),
            // Nest another collector, to transform the list of values 
            // of each group into something else. Note that toMap does not 
            // buffer a collection of values for a key. It is designed to 
            // map a key to each value. But, it allows user to merge  
            // merge conflicting values if needed.
            toMap(
                // Same than groupby, we specify how to extract key from value. 
                t -&gt; t.to, 
                // Here, specify that the value associated to the key is the 
                // object we&#39;ve extracted key from. Here, wwe could have changed
                // it to something else (a text representation, for example).
                t -&gt; t,
                // toMap will bind one key to one value. We have to solve 
                // conflict ourself. Here, author decided to drop arbitrarily 
                // one of the values.
                (x, y) -&gt; y,
                // As for groupby operator, author forces a map implementation 
                // dedicated to enum keys.
                () -&gt; new EnumMap&lt;&gt;(Phase.class))));

Finally, official stream collection documentation provide quite some explanation.

I'm not sure if my explanation is enough, you can give remarks in comments.

答案2

得分: 0

以下是翻译好的内容:

这里创建的是一个映射到映射的地图,它在从一个状态转换到另一个状态时提供了转换方法。如果没有使用 lambdasstreams,代码可能如下所示:

public static Map<Phase, Map<Phase, Transition>> init() {
    Map<Phase, Map<Phase,Transition>> outerMap = new EnumMap<>(Phase.class);
    for (Transition v : Transition.values()) {
        Map<Phase, Transition> innerMap = outerMap.get(v.from);
        if (innerMap == null) {
            innerMap = new EnumMap<>(Phase.class);
            outerMap.put(v.from, innerMap);
        }
        innerMap.put(v.to, v);
    }
    return outerMap;
}

接下来是关于流的解释。

首先从 Transition 枚举值开始一个流。在这里的所有情况下,t 是一个 Transition 类型。

public static Map<Phase, Map<Phase, Transition>> map = Stream
        .of(values())

然后使用 Collectors.groupingBy() 方法将它们收集到一个 EnumMap 中。这里的所有键将来自于 Transition.from 字段。这个外部地图的值将是一个 EnumMap

        .collect(Collectors.groupingBy(t -> t.from,
                () -> new EnumMap<>(Phase.class),

接下来,下游收集器会用另一个地图填充刚刚创建的地图。内部地图将使用以下设置。

  • 以 Transition.to 字段作为键 - t.to
  • 结果 Transition 类型作为值,即 t
                Collectors.toMap(t -> t.to, t -> t,

下一个语句只是一个合并函数,在这个特定应用程序中没有使用,所以只选择了一个值。

                        (x, y) -> x,

最后,指定一个 Supplier 来创建 innerMap 的类型。

                        () -> new EnumMap<>(Phase.class))));

当使用来自 Phase 枚举的参数调用 Transition.from() 时,将引用地图以显示 Transition 过程。

英文:

What is being create here is a Map of Maps that provide the Transition method when going from one state, to another. If it were done without lambdas or streams it might look like the following:

	public static Map&lt;Phase, Map&lt;Phase, Transition&gt;&gt; init() {
		Map&lt;Phase, Map&lt;Phase,Transition&gt;&gt; outerMap = new EnumMap&lt;&gt;(Phase.class);
		for (Transition v : Transition.values()) {
            Map&lt;Phase, Transition&gt; innerMap = outerMap.get(v.from);
            if (innerMap == null) {
	     		innerMap = new EnumMap&lt;&gt;(Phase.class);
		     	outerMap.put(v.from, innerMap);
            }
			innerMap.put(v.to, v);
		}
		return outerMap;
	}

What follows is the Stream explanation.

Start with a stream of the Transition enum values. In all cases here, t is a Transition type.

		public static Map&lt;Phase, Map&lt;Phase, Transition&gt;&gt; map = Stream
				.of(values())

Then they are collected into a EnumMap using the Collectors.groupingBy() method. All the keys here will be taken from the Transition.from field. The value for this outer map will be an EnumMap

				.collect(Collectors.groupingBy(t -&gt; t.from,
						() -&gt; new EnumMap&lt;&gt;(Phase.class),

Next, the downstream collector, now populates that newly created map with another map. The inner map will use the following.

  • Transition.to field for keys - t.to
  • The resulting Transition type for values which is just t
						Collectors.toMap(t -&gt; t.to, t -&gt; t,

This next statement is simply a merge function and it isn't used in this particular application so a just chose a value.

								(x, y) -&gt; x,

Finally, specify a Supplier to for the type of innerMap to create.

                               () -&gt; new EnumMap&lt;&gt;(Phase.class))));
		

When Transition.from() is invoked using arguments from the Phase enum the map is referenced to display the Transition process.

huangapple
  • 本文由 发表于 2020年4月10日 01:46:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/61127119.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定