Transform and filter a Java Map with streams

前端 未结 6 1749
既然无缘
既然无缘 2021-01-31 02:40

I have a Java Map that I\'d like to transform and filter. As a trivial example, suppose I want to convert all values to Integers then remove the odd entries.

Map         


        
6条回答
  •  萌比男神i
    2021-01-31 03:18

    Another way to do this is to remove the values you don't want from the transformed Map:

    Map output = input.entrySet().stream()
            .collect(Collectors.toMap(
                    Map.Entry::getKey,
                    e -> Integer.parseInt(e.getValue()),
                    (a, b) -> { throw new AssertionError(); },
                    HashMap::new
             ));
    output.values().removeIf(v -> v % 2 != 0);
    

    This assumes you want a mutable Map as the result, if not you can probably create an immutable one from output.


    If you are transforming the values into the same type and want to modify the Map in place this could be alot shorter with replaceAll:

    input.replaceAll((k, v) -> v + " example");
    input.values().removeIf(v -> v.length() > 10);
    

    This also assumes input is mutable.


    I don't recommend doing this because It will not work for all valid Map implementations and may stop working for HashMap in the future, but you can currently use replaceAll and cast a HashMap to change the type of the values:

    ((Map)input).replaceAll((k, v) -> Integer.parseInt((String)v));
    Map output = (Map)input;
    output.values().removeIf(v -> v % 2 != 0);
    

    This will also give you type safety warnings and if you try to retrieve a value from the Map through a reference of the old type like this:

    String ex = input.get("a");
    

    It will throw a ClassCastException.


    You could move the first transform part into a method to avoid the boilerplate if you expect to use it alot:

    public static > M transformValues(
            Map old, 
            Function f, 
            Supplier mapFactory){
        return old.entrySet().stream().collect(Collectors.toMap(
                Entry::getKey, 
                e -> f.apply(e.getValue()), 
                (a, b) -> { throw new IllegalStateException("Duplicate keys for values " + a + " " + b); },
                mapFactory));
    }
    

    And use it like this:

        Map output = transformValues(input, Integer::parseInt, HashMap::new);
        output.values().removeIf(v -> v % 2 != 0);
    

    Note that the duplicate key exception can be thrown if, for example, the old Map is an IdentityHashMap and the mapFactory creates a HashMap.

提交回复
热议问题