在流映射中使用 Java 可选

Using Java Optional within stream mapping

我有这样的代码:

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);
        }
    }
}

理想情况下,我想简化它并避免使用 list.stream().map( *blah, blah, blah* )doSomethingWithObject 检查 null 如果对象不是 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(不带任何参数,returns 也没有任何参数)以及 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 buckets/partitions 中。之后,按要求处理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));