代码之家  ›  专栏  ›  技术社区  ›  Birchlabs

如何使Map的每个可选值都是可选的

  •  2
  • Birchlabs  · 技术社区  · 8 年前

    我有一个具有许多可选值的映射:

    Map<MyCoolKey, Optional<MyCoolValue>>
    

    我想把这张地图转换成 Optional<Map<>> :

    Optional<Map<MyCoolKey, MyCoolValue>>
    
    • 如果每 Optional<MyCoolValue> 出席:the 可选地图; 应该在场。
    • 如果 任何 可选<MyCoolValue> 不存在: 可选地图; 应该不在场。

    我试过了,我怀疑我的代码会起作用,但有点冗长:

    final Map<MyCoolKey, Optional<MyCoolValue>> myCoolMap;
    final Optional<Map<MyCoolKey, MyCoolValue>> optionalMap = myCoolMap
                    .entrySet()
                    .stream()
                    .map(e -> e
                            .getValue()
                            .flatMap(value -> Optional.<Map.Entry<MyCoolKey, MyCoolValue>>of(
                                    new AbstractMap.SimpleEntry<>(
                                            e.getKey(),
                                            value
                                    )
                            ))
                    )
                    .collect(
                            () -> Optional.<Map<MyCoolKey, MyCoolValue>>of(new HashMap<>()),
                            (optAcc, optEntry) -> optAcc.flatMap(
                                    acc -> optEntry.map(
                                            entry -> {
                                                acc.put(entry.getKey(), entry.getValue());
                                                return acc;
                                            })
                            ),
                            (optAcc1, optAcc2) -> optAcc1.flatMap(
                                    acc1 -> optAcc2.map(
                                            acc2 -> {
                                                acc1.putAll(acc2);
                                                return acc1;
                                            }
                                    )
                            )
                    );
    

    有更好的办法吗?”“更好”意味着正确,表现,美丽。我想要一个能在一个流中完成整个操作的答案。

    4 回复  |  直到 8 年前
        1
  •  4
  •   Danon TylerDurden    8 年前

    下面是纯流解决方案的一个例子(没有 if s和三元运算符)

    final Map<MyCoolKey, Optional<MyCoolValue>> myCoolMap = new HashMap<>();
    
    Optional<Map<MyCoolKey, MyCoolValue>> output = Optional.of(myCoolMap)
        .filter(map -> map.values().stream().allMatch(Optional::isPresent))
        .map(map -> map
            .entrySet()
            .stream()
            .collect(toMap(
                Map.Entry::getKey,
                entry -> entry.getValue().get()
            ))
        );
    

    这并不过分复杂-过滤和映射是流的用途!

        2
  •  2
  •   Tomasz Linkowski    8 年前

    以下方法完成此工作:

    Map<MyCoolKey, Optional<MyCoolValue>> input;
    Optional<Map<MyCoolKey, MyCoolValue>> output = convertMapWithOptionals(input);
    

    我想出了两种方法:

    1) 勉强:首先检查所有,然后开始转换

    <K, V> Optional<Map<K, V>> convertMapWithOptionals(Map<K, Optional<V>> map) {
        if (!map.values().stream().allMatch(Optional::isPresent)) {
            return Optional.empty();
        }
        return Optional.of(map.entrySet().stream().collect(Collectors.toMap(
                Map.Entry::getKey, entry -> entry.getValue().get()
        )));
    }
    

    2) 急切:开始转换并在必要时中止

    <K, V> Optional<Map<K, V>> convertMapWithOptionals(Map<K, Optional<V>> map) {
        Map<K, V> result = new HashMap<>();
        for (Map.Entry<K, Optional<V>> entry : map.entrySet()) {
            if (!entry.getValue().isPresent()) {
                return Optional.empty();
            }
            result.put(entry.getKey(), entry.getValue().get());
        }
        return Optional.of(result);
    }
    
        3
  •  1
  •   Vlad Bochenin Rom4in    8 年前

    如果我理解正确的问题,你可以使用 reduce 而不是 collect . 它会让你改变 accumulator 在收集过程中。

        Optional<Map<MyCoolKey, MyCoolValue>> optionalMap = myCoolMap
                .entrySet()
                .stream()
                .reduce(Optional.<Map<MyCoolKey, MyCoolValue>>of(new HashMap<>()),
                        (acc, entry) -> {
                            if (!acc.isPresent()) {
                                return acc;
                            }
                            if (!entry.getValue().isPresent()) {
                                return Optional.empty();
                            }
                            acc.get().put(entry.getKey(), entry.getValue().get());
                            return acc;
                        },
                        (acc, acc1) -> {
                            if (acc.isPresent() && acc1.isPresent()) {
                                acc.get().putAll(acc1.get());
                                return acc;
                            }
                            return Optional.empty();
                        }
                );
    
        4
  •  0
  •   Michael    8 年前

    我认为你把所有的逻辑都放在一条小溪里,把事情搞得太复杂了。把空的过滤掉 Optional 并查看元素的数目是否相同:

    Optional<Map<MyCoolKey, MyCoolValue>> result = Optional.of(
        map.entrySet()
            .stream()
            .filter(e -> e.getValue().isPresent())
            .collect(
                Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get())
            )
    )
    .filter(x-> x.size() == map.size());