正确的lambda过滤器实现

问题描述 投票:2回答:5

我有一个需要的案例

  • map是一个对象,如果映射函数抛出异常,我将其映射到null
  • filternull对象的映射流,如果为null则抛出异常,否则收集到List。

我怎么做到这一点?

list.stream().map(ob-> {
    try {
        // cannot throw only catch 
        return function(ob);
    } catch (Exception e) {
        log.error(e);
        return null;
    }            
}).filter(Objects::isNull).findFirst().orElseThrow(Exception::new);

现在我的问题是我应该如何在throw new Exception()null上调整/重构上述lambda到collect(Collectors.toList())

java list lambda java-8 java-stream
5个回答
6
投票

如果您打算报告异常(这是一个好主意),您应该首先将它映射到null。由于某些功能接口不允许抛出已检查的异常,因此应将其重新抛出包含在未经检查的异常中:

try {
    List<Object> result = list.stream().map(ob-> {
        try {
            // cannot throw checked exception types
            return function(ob);
        } catch(Exception e) {
            throw new CompletionException(e);
        }
    }).collect(Collectors.toList());
} catch(CompletionException ex) {
    throw (Exception)ex.getCause();
}

关键点在于,这将抛出原始异常,其中包含所有信息,而不是通过new Exception()创建一个根本不包含原因信息的新实例。

请注意,对于某些情况,已经存在专用的异常类型,例如UncheckedIOException包裹IOException。在其他情况下,声明自己的未经检查的异常类型可能更清晰,以确保它不会与应用程序的其他组件抛出的其他异常混淆。


3
投票

如果地图包含null密钥的非空集合,则可以按谓词进行分区并抛出异常:

Map<Boolean, List<String>> resultMap = list.stream().map(ob-> {
    try {
        return function(ob);
    } catch (Exception e) {
        return null;
    }
}).collect(Collectors.partitioningBy(Objects::isNull));

if(!resultMap.get(Boolean.TRUE).isEmpty()) {
    throw new Exception();
}

return resultMap.get(Boolean.FALSE);

Collectors.partitioningBy(Objects::isNull)将返回一个Map<Boolean, List<T>>,其中true将被映射到一个列表,其中所有元素与谓词(Objects::isNull)匹配,而false则不会。

如果true集合不为空,您知道可以引发异常。


3
投票

如果我检测到我不需要迭代下一个元素,我会抛出异常并立即离开流处理。如果它无助,为什么继续执行逻辑呢?

所以我不会在这种情况下使用内置的map()而不是流。我认为通过引入一个简单的方法进行映射会使事情变得非常易读:

try{
    return map(list);
} 
catch (Exception e) {
    throw new AnyExceptionYouWant(e);
}

// helper method
List<Bar> map (List<Foo> list) throws Exception{
   List<Bar>> bars = new ArrayList<>();       
   for (Foo foo : list){
          bars.add(function(foo));
   }
   return bars;
 }

如果你想使用可读且易于维护的流,你可能不应该在function()中抛出任何异常。例如,您可以返回Optionals列表,因此处理流中的空案例会很简单。


3
投票

好吧,有可能在lambdas中使用try-catch条款,但不建议这样做,因为lambdas应尽可能短。

将映射器分隔为新方法,然后在lambda中调用它。

private static final <T, R> R tryMapOrElseNull(T t) {
    try {
        return function(t);
    } catch (Exception e) {
        this.log.error(e);
        return null;
    }
}

然后使用该方法作为Stream::map方法中的方法参考。首先,收集新映射的元素,然后只需检查null

newList = list.stream().map(MyClass::safeMap).collect(Collectors.toList());

if (newList.contains(null)) {
    throw new Exception();
}

1
投票

我分两步完成,先收集到一个列表:

List<T> result = list.stream().map(ob -> {
                try {
                    // cannot throw only catch, since lambda expression 
                    return function(ob);
                } catch (Exception e) {
                    log.error(e);
                    return null;
                }
            }).collect(toList()); 

其中T是映射到的元素的类型。

然后检查无效:

if(result.contains(null)) {/* throw exeception... */}
else { /* do something else */}
© www.soinside.com 2019 - 2024. All rights reserved.