java8 stream style for retrieving a inner part of

2019-04-09 22:09发布

问题:

For example, given a map like below:

{
  "k1": {
    "k2": {
      "k3": {
        "k4": "v"
      }
    }
  }
}

and a field list ["k1","k2","k3"], I need to retrieve the part {"k4": "v"}.

Below is my java7-style code:

// Ignore the map building code.
Map map1 = new HashMap();
Map map2 = new HashMap();
Map map3 = new HashMap();
Map map4 = new HashMap();
map4.put("k4", "v");
map3.put("k3", map4);
map2.put("k2", map3);
map1.put("k1", map2);
Map map = map1;
System.out.println(map); //=> {k1={k2={k3={k4=v}}}}

// Code to be transformed to java8 style
List<String> fields = Arrays.asList("k1", "k2", "k3");
for(String field: fields) {
    map = (Map) map.get(field);
}
System.out.println(map); //=> {k4=v}

Then how to transform above code to java 8 stream style?

回答1:

I don’t think that there is any benefit in converting this into a functional style; the loop is fine and precisely expresses what you are doing.

But for completeness, you can do it the following way:

map = (Map)fields.stream()
    .<Function>map(key -> m -> ((Map)m).get(key))
    .reduce(Function.identity(), Function::andThen).apply(map);

This converts each key to a function capable of doing a map lookup of that key, then composes them to a single function that is applied to you map. Postponing the operation to that point is necessary as functions are not allowed to modify local variables.

It’s also possible to fuse the map operation with the reduce operation, which allows to omit the explicit type (<Function>):

map = (Map)fields.parallelStream()
 .reduce(Function.identity(), (f, key)->f.andThen(m->((Map)m).get(key)), Function::andThen)
 .apply(map);

Maybe you recognize now, that this is a task for which a simple for loop is better suited.



回答2:

How about?

fields.stream().reduce(map1, (m, key) -> (Map) m.get(key), (a, b) -> a);