【问题标题】:How to add a single Key value to existing Java 8 stream?如何向现有的 Java 8 流添加单个键值?
【发布时间】:2019-06-27 18:27:40
【问题描述】:

这是我填充静态地图的方法

public static final Map<String, FooBar> mapEnum = 

Arrays.stream(FooBarEnum.values())
            .collect(Collectors.toMap(e-> StringUtils.upperCase(e.name), e -> e));

我想向此地图添加另一个键值。

mapEnum.put("xx", FooBar.A);

这里是枚举

public enum FooBar {
   A("a"), B("b"), C("c");
}

地图构建后我的静态地图是这样的

{"a":FooBar.A, "b": FooBar.B, "c": FooBar.C, "xx": Foobar.A}

是否可以将显式 put 调用包含在 Collectors.toMap() 中?

【问题讨论】:

    标签: java java-8 java-stream collectors


    【解决方案1】:

    如果您愿意使用第三方库,您可以使用 Eclipse Collections 创建与 Stream 内联的静态 ImmutableMap

    public static final ImmutableMap<String, FooBar> MAP_ENUM =
            Arrays.stream(FooBar.values())
                    .collect(Collectors2.toMap(FooBar::getName, fooBar -> fooBar))
                    .withKeyValue("xx", FooBar.A)
                    .toImmutable();
    
    public enum FooBar {
        A("a"), B("b"), C("c");
    
        private String name;
    
        FooBar(String name) {
            this.name = name;
        }
    
        public String getName() {
            return this.name;
        }
    }
    

    您还可以使用原生 Eclipse Collections API 稍微简化代码。

    public static final ImmutableMap<String, FooBar> MAP_ENUM =
            ArrayAdapter.adapt(FooBar.values())
                    .groupByUniqueKey(FooBar::getName)
                    .withKeyValue("xx", FooBar.A)
                    .toImmutable();
    

    注意:我是 Eclipse Collections 的提交者

    【讨论】:

    • 非常好的解决方案。以前不知道 Eclipse Collections。
    【解决方案2】:

    实际上,我认为没有必要为此使用 Java Streams。您只需使用 static 块来初始化其中的 mapEnumput 附加值:

    public static final Map<String, FooBar> mapEnum;
    
    static {
        mapEnum = Arrays.stream(FooBar.values())
                .collect(Collectors.toMap(FooBar::getName, Function.identity()));
        mapEnum.put("xx", FooBar.A);
        // ...
    }
    

    Collectors.toMap():不保证返回的 {@code Map} 的类型、可变性、可序列化性或线程安全性。

    为了保证Collectors.toMap()返回的Map的可变性,所以你以后可以使用Map.put()更好地使用这个:

    Arrays.stream(FooBar.values())
            .collect(Collectors.toMap(Function.identity(), Function.identity(), (a, b) -> a, HashMap::new));
    

    如果你真的想使用 java 流,你可以使用这个:

    public static final Map<String, FooBar> mapEnum = Stream.concat(
            Stream.of(FooBar.values()).map(e -> Map.entry(e.getName(), e)),
            Stream.of(Map.entry("xx", FooBar.A))
    ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    

    或者,如果您还想将所有名称添加到枚举值本身,您可以像这样更改您的类:

    public static enum FooBar {
        A("a", "xx"), B("b"), C("c");
    
        private String[] names;
    
        FooBar(String... names) {
            this.names = names;
        }
    
        public String[] getNames() {
            return names;
        }
    }
    

    并使用它来创建地图:

    public static final Map<String, FooBar> mapEnum = Stream.of(FooBar.values())
            .flatMap(e -> Arrays.stream(e.getNames()).map(n -> Map.entry(n, e)))
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    

    在 Java 9 之前使用 new AbstractMap.SimpleEntry&lt;&gt;() 而不是 Map.entry()。如果您需要对地图进行排序,请使用LinkedHashMap::newCollectors.toMap()

    【讨论】:

    • “确保看跌期权的可变性”是什么意思
    • 所以如果你不明确地做 HashMap::new,我们会得到什么?不可变地图?
    • @brainstorm 查看源代码,它是HashMap,但正如我在共享的文档中所说,在功能版本中不能保证是HashMap。我只是想提一下。在大多数情况下,我个人会坚持使用默认的toMap()
    • 另一个问题是 FooBar::getName?我如何获得大写版本?即像这样的“FooBar::StringUtils.upperCase(getName)”
    • @brainstorm 这只是o -&gt; o.getName() 的简短版本。您可以使用 `o -> StringUtils.upperCase(o.get name());就像你的问题一样。
    【解决方案3】:

    您可以使用Collectors::collectAndThen 修改生成的地图

    Arrays.stream(FooBarEnum.values())
            .collect(Collectors.collectAndThen(
                        Collectors.toMap(e-> StringUtils.upperCase(e.name), 
                            Function.identity()), FooBarEnum::addCustom));
    

    以下方法在枚举中

    static Map<String, FooBar> addCustom(Map<String, FooBarEnum> map) {
        map.put("xx", FooBar.A);
        return map;
    }
    

    【讨论】:

    • Collectors.toMap 并没有说生成的Map 是可变的......但是这对于之后的单个map.put(...) 来说似乎太多了
    【解决方案4】:

    您不能直接将其传递给 Collectors.toMap()。您可以在 javadocs 中查看所有可用的覆盖:https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#toMap-java.util.function.Function-java.util.function.Function-

    但是,在使用 Stream.concat 调用 toMap 之前,您可以确保您的流具有构建映射所需的所有对。您将枚举中的对与要添加的手动对连接起来。

    我的独立代码必须定义 Pair 类,但由于您使用 StringUtils,我想您有一个已经包含 Pair 的库,因此您不需要定义它。

    代码:

    import java.util.*;
    import java.util.stream.*;
    
    public class Main {
    
        private static enum  FooBar {
            A("a"), B("b"), C("c");
            private String name;
            FooBar(String name) {
                this.name = name;
            }
        }
    
        public static class Pair {
            String a;
            FooBar b;
            Pair(String a, FooBar b) {
                this.a = a;
                this.b = b;
            }
        }
    
        public static void main(String [] args) {
            System.out.println(
                Stream.concat( 
                    Arrays.stream(FooBar.values()).map(e -> new Pair(e.name.toUpperCase(), e)),
                    Stream.of(new Pair("xx", FooBar.A))
                )
                .collect(Collectors.toMap(pair -> pair.a, pair -> pair.b))
            );
        }
    
    }
    

    输出:

    {xx=A, A=A, B=B, C=C}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多