在流映射中使用 Java Optional

问题描述

我有这样的代码

public void processList(List<String> list) {

    for (String item : list) {
        Object obj = getobjectForString(item);
        if (obj != null) {
            doSomethingWithObject(obj);
        } else {
            System.err.println("Object was null for " + item);
        }
    }
}

理想情况下,我想简化这一点并避免使用 null 进行 list.stream().map( *blah,blah,blah* ) 检查,如果对象不是 doSomethingWithObject,则使用 null,否则记录错误(通过在可选上使用 orElse 方法)。我对这个 Java 8 功能不是很精通,不确定是否有一种很好的、​​灵活的方式来做我想做的事情。建议?

编辑以添加失败的尝试:

list.stream()
    .map(p -> getobjectForString(p))
    .map(Optional::ofNullable)
    .forEach(
        p -> p.ifPresentOrElse(
            r -> doSomethingWithObject(r),() -> System.err.println("Object was null")
        ));

即使该代码按照我想要的方式运行,它仍然不会像我希望的那样将原始列表中的字符串附加到错误消息中。但是,尝试使用这样的流来完成可能太复杂了。

解决方法

即使在转换之后,我们也应该传播 item。巧妙的方法是使用元组或对。

我使用了 vavr 函数库中的 Tuple 来做同样的事情。以下是供您参考的代码

list.stream()
                .map(p -> Tuple.of(p,getObjectForString(p)).map2(Optional::ofNullable))
                .forEach(p -> p._2.ifPresentOrElse(
                            r -> doSomethingWithObject(r),() -> System.err.println("Object was null" + p._1))
                );
,

即使下面的方法没有避免您在问题中想要的 null 检查,但这只是实现相同结果的另一种方法。 (唯一的好处是它节省了 1-2 行代码!)。

下面的代码使用 Runnable(不接受任何参数,也不返回任何内容)和 Java 8 的 Function

注意:我仍然推荐普通的 for 循环 :-),因为我相信下面的内容可能看起来很花哨,但 for 循环更容易理解这种特殊情况。

Function<String,Runnable> func = item -> {
    Object obj = getObjectForString(item);
    return (obj != null) ? ( () -> doSomethingWithObject(obj))
                         : ( () -> System.err.println("Object was null for " + item));        
};

list.stream().map(func).forEach(Runnable::run);
,

另一种方法是根据项目是否具有关联对象将项目收集到单独的 2 个存储桶/分区中。之后,根据需要处理 2 个桶:

final Boolean HAS_OBJECT = Boolean.FALSE;

Map<Boolean,List<String>> partitionedMap = list.stream()
        .collect(Collectors.partitioningBy(item -> !Objects.isNull(getObjectForString(item))));

partitionedMap.get(HAS_OBJECT).stream()
    .map(item -> getObjectForString(item))
    .forEach(obj -> doSomethingWithObject(obj));

partitionedMap.get(!HAS_OBJECT)
    .forEach(item -> System.err.println("Object was null for " + item));