Streams Java 8 - 重构流代码



我需要重构下面的流代码:

    List<Map<String, Integer>> list5 = new ArrayList<>();
    map3 = new HashMap<>();
    map3.put("foo", 1);
    map3.put("bar", 2);
    map3.put("zzz", 6);
    list5.add(map3);
    map3 = new HashMap<>();
    map3.put("foo", 3);
    map3.put("bar", 4);
    map3.put("zzz", null);
    list5.add(map3);
    //new list with processed maps
    List<Map<String, Integer>> list6 = list5.stream()
            .map(hashmap -> {
                Map<String, Integer> newMap = hashmap.entrySet().stream()
                        .collect(HashMap::new, (m, v) -> {
                            if (v.getKey().equals("bar")) {
                                m.put(v.getKey(), v.getValue() * 2);
                            } else {
                                m.put(v.getKey(), v.getValue());
                            }
                        }, HashMap::putAll);
                return newMap;
            })
            .collect(toList());
    System.out.println(list6);

我需要一种方法来仅从上面的流代码中提取/重构以下逻辑,因为这部分只会在我拥有的其他映射列表中更改:

if (v.getKey().equals("bar")) {
    m.put(v.getKey(), v.getValue() * 2);
} else {
    m.put(v.getKey(), v.getValue());
}

使用 IntelliJ,它将一个 biconsumer 添加到 main(( 本身,这在这里不是预期的,并删除了代码。我需要一种方法来单独提取它,如下所示:

List<Map<String, Integer>> list6 = list5.stream()
            .map(hashmap -> {
                Map<String, Integer> newMap = hashmap.entrySet().stream()
                        .collect(HashMap::new, (m, v) -> {
                            biconsumerLogic1.accept(m, v);
                        }, HashMap::putAll);
                return newMap;
            })
            .collect(toList());

biconsumerLogic1是一个独立的功能接口,如下所示:

BiConsumer biconsumerLogic1() {
    accept(m, v) {
         //logic goes here...
    }
}

我该如何实现?任何指示都值得赞赏。

谢谢。。

看,问题出在使用流上。你已经过度设计了它,你不需要它的大部分。

看:

List<Map<String, Integer>> newList = new ArrayList<>(list);
newList.replaceAll(eachMap -> {
  Map<String, Integer> map = new HashMap<>(eachMap);
  map.computeIfPresent("bar", (k,v) -> v * 2);
  return map;
});

要替换该操作,您需要执行以下操作:

BiFunction<String, Integer, Integer> action = (k,v) -> v * 2;
newList.replaceAll(eachMap -> {
  Map<String, Integer> map = new HashMap<>(eachMap);
  map.computeIfPresent("bar", action);
  return map;
});

现在你可以把整个东西提取到一个单独的方法中,并接受listaction(也可能是"bar"变量(作为参数,这里你就有了你的值替换器。

我认为有更好的方法来做你实际正在做的事情。但是严格回答如何提取和重构指示的代码段,你可以做这样的事情:

static <K, V> List<Map<K, V>> process(
        List<Map<K, V>> source,
        BiConsumer<? super Map<K, V>, ? super Map.Entry<K, V>> accumulator) {
    return source.stream()
        .map(m -> m.entrySet().stream()
            .collect(HashMap::new, accumulator, Map::putAll))
        .collect(Collectors.toList());
}

用法:

List<Map<String, Integer>> list6 = process(
    list5,
    (m, e) -> m.put(
        e.getKey(), 
        e.getKey().equals("bar") ? e.getValue() * 2 : e.getValue()));

以下是我使用BiConsumer进行重构的版本:

psv main(...) {
    List<Map<String, Integer>> newList = processMapData(origList, accumulator());
    System.out.println(newList);
}
private static List<Map<String, Integer>> processMapData(List<Map<String, Integer>> origList, BiConsumer<HashMap<String, Integer>, Map.Entry<String, Integer>> accumulator) {
    return origList.stream()
                .map(hashmap -> {
                    Map<String, Integer> newMap = hashmap.entrySet().stream()
                            .collect(HashMap::new, accumulator, HashMap::putAll);
                    return newMap;
                })
                .collect(toList());
}
private static BiConsumer<HashMap<String, Integer>, Map.Entry<String, Integer>> accumulator() {
    return (m, v) -> {
        m.put(v.getKey(), v.getKey().equals("bar") ? v.getValue() * 2: v.getValue());
    };
}

我可以利用更多的控制并动态注入累加器来processMapData功能。感谢@Federico的BiConsumer功能建议。

怎么样:

list5.stream().forEach(map -> map.replaceAll((k,v) -> k.equals("bar") ? v * 2 : v));

这将就地替换值。

如果您绝对需要一张新地图:

list6 = list5.stream()
    .map(m -> m.entrySet().stream()
        .collect(toMap(Map.Entry::getKey, e -> e.getValue() * (e.getKey().equals("bar") ?  2 : 1))))
    .collect(toList());

如果要重构,有两个选项:

选项 1:

创建一个BiFunction,例如:

public static BiFunction<String, Integer, Integer> valueCalculator() {
    return (k,v) -> k.equals("bar") ? v * 2 : v;
}

并使用:

list5.stream().forEach(map -> map.replaceAll(valueCalculator()));

选项 2:

创建一个方法:

public Integer valueCalculator(String key, Integer value) {
    return key.equals("bar") ? value * 2 : value;
}

若要使用,请使用方法引用

list5.stream().forEach(map -> map.replaceAll(this::valueCalculator));

如果是static方法:

list5.stream().forEach(map -> map.replaceAll(MyClass::valueCalculator));

我更喜欢选项 2,因为它更容易理解、测试和调试。

最新更新