Search code examples
javajava-8java-streamcollectors

How to add a single Key value to existing Java 8 stream?


Here is what I do to populate my static map

public static final Map<String, FooBar> mapEnum = 

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

I want to add another single key-value to this map.

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

Here is the enum

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

My static map will look like this after map is constructed

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

Is it possible to include the explicit put call into Collectors.toMap()?


Solution

  • I actually don't see the need to use Java Streams for that. You simply can use the static block to initialize mapEnum and put additional values in it:

    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(): There are no guarantees on the type, mutability, serializability, or thread-safety of the {@code Map} returned.

    To ensure the mutability of the Map returned by Collectors.toMap(), so you can use Map.put() afterwards better use this:

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

    If you really want to use java streams you can use this:

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

    Or if you also want to add all names to the enum value itself you can change your class like this:

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

    And use this to create the map:

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

    Prior to Java 9 use new AbstractMap.SimpleEntry<>() instead of Map.entry(). If you need the map to be sorted use LinkedHashMap::new with Collectors.toMap().