Java 如何使映射在其每个可选值是否存在时成为可选的

Java 如何使映射在其每个可选值是否存在时成为可选的,java,java-stream,Java,Java Stream,我有一个带有许多可选值的映射: Map 我想将此地图转换为可选的: 可选 如果每个可选都存在:则可选应该存在 如果任何可选不存在:可选应不存在 我尝试过这样做,我怀疑我的代码会起作用,但这有点冗长: 最终地图mycolmap; 最终可选optionalMap=MyColMap .entrySet() .stream() .地图(e->e .getValue() .flatMap(值->可选.of( 新建AbstractMap.SimpleEntry( e、 getKey(), 价值 )

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

Map
我想将此地图转换为
可选的

可选
  • 如果每个
    可选
    都存在:则
    可选
    应该存在
  • 如果任何
    可选
    不存在:
    可选
    应不存在
我尝试过这样做,我怀疑我的代码会起作用,但这有点冗长:

最终地图mycolmap;
最终可选optionalMap=MyColMap
.entrySet()
.stream()
.地图(e->e
.getValue()
.flatMap(值->可选.of(
新建AbstractMap.SimpleEntry(
e、 getKey(),
价值
)
))
)
.收集(
()->可选.of(新HashMap()),
(optAcc,optEntry)->optAcc.flatMap(
acc->optEntry.map(
条目->{
acc.put(entry.getKey(),entry.getValue());
返回acc;
})
),
(optAcc1,optAcc2)->optAcc1.flatMap(
acc1->optAcc2.map(
acc2->{
acc1.putAll(acc2);
返回acc1;
}
)
)
);

有更好的方法吗?“更好”意味着正确性、性能和美观。我希望答案能够在一个流中完成整个操作。

以下方法可以完成此任务:

Map<MyCoolKey, Optional<MyCoolValue>> input;
Optional<Map<MyCoolKey, MyCoolValue>> output = convertMapWithOptionals(input);
Map输入;
可选输出=convertMapWithOptionals(输入);
我提出了这种方法的两种“风格”:

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()
    )));
}
可选convertMapWithOptionals(地图地图){
如果(!map.values().stream().allMatch(可选::isPresent)){
返回可选的.empty();
}
返回可选的.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);
}
可选convertMapWithOptionals(地图地图){
映射结果=新的HashMap();
对于(Map.Entry:Map.entrySet()){
如果(!entry.getValue().isPresent()){
返回可选的.empty();
}
put(entry.getKey(),entry.getValue().get());
}
返回可选。of(结果);
}

这里是纯流溶液的一个例子(没有
if
s和三元运算符)

final Map mycolmap=new HashMap();
可选输出=可选的.of(myColMap)
.filter(map->map.values().stream().allMatch(可选::isPresent))
.map(地图->地图)
.entrySet()
.stream()
.收集(
Map.Entry::getKey,
entry->entry.getValue().get()
))
);

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

我认为你试图把所有的逻辑都放在一条流中,这太复杂了。只需过滤掉空的
可选
s,然后查看元素的数量是否相同:

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());
Optional result=Optional.of(
map.entrySet()
.stream()
.filter(e->e.getValue().isPresent())
.收集(
Collectors.toMap(Map.Entry::getKey,e->e.getValue().get())
)
)
.filter(x->x.size()==map.size());

如果我对问题理解正确,您可以使用
reduce
而不是
collect
。它允许您在采集期间更改
累加器

    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();
                    }
            );
可选optionalMap=MyColMap
.entrySet()
.stream()
.reduce(可选的.of(新的HashMap()),
(acc,条目)->{
如果(!acc.isPresent()){
返回acc;
}
如果(!entry.getValue().isPresent()){
返回可选的.empty();
}
acc.get().put(entry.getKey(),entry.getValue().get());
返回acc;
},
(acc,acc1)->{
如果(根据isPresent()和&acc1.isPresent()){
acc.get().putAll(acc1.get());
返回acc;
}
返回可选的.empty();
}
);

因此,如果此映射长度为10000个项目,您还是要映射它,然后检查是否要返回它?@Danon您的解决方案将在所有元素上迭代两次。一个简单的不必要的映射并不比这更昂贵。这会给条目增加更多,但过早的优化是万恶之源,所以谁在乎呢。此外,10000项也没什么,Michael,你可以将其内联到一条语句中:
result=Optional.of().filter(tmp->tmp.size()==map.size())
@MalteHartwig是的,我更喜欢它。谢谢@迈克尔:我同意@Danon。请注意,如果映射有很多元素,即使第一个值是
Optional.empty()
,您也将收集整个映射,而不是使用短路操作(因此它不会测试所有剩余元素)。我发现此解决方案非常不直观且不可读,即使它返回的
Optional.empty()中有一些值
    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();
                    }
            );