java8流样式,用于通过字段列表检索地图的内部部分?

Run*_*Run 4 functional-programming java-8 java-stream

例如,给出如下地图:

{
  "k1": {
    "k2": {
      "k3": {
        "k4": "v"
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

和一个字段列表["k1","k2","k3"],我需要检索该部分{"k4": "v"}.

下面是我的java7风格的代码:

// 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}
Run Code Online (Sandbox Code Playgroud)

那么如何将上面的代码转换为java 8流式呢?

Hol*_*ger 8

我不认为将其转化为功能性风格有任何好处; 循环很好,精确地表达你正在做的事情.

但为了完整起见,您可以通过以下方式执行此操作:

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

这会将每个键转换为能够对该键进行地图查找的函数,然后将它们组合成应用于您的单个函数map.将操作推迟到该点是必要的,因为不允许函数修改局部变量.

也可以将map操作与reduce操作融合,这允许省略显式类型(<Function>):

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

也许你现在认识到,这是一个简单for循环更适合的任务.

  • @tobias_k:这可以完美地并行工作,因为将流转换为并行只会并行化函数组合,而最终的`apply`调用始终是顺序的(并且与流无关). (3认同)
  • @Holger你的答案让我头晕目眩.这非常好 (3认同)