正确的lambda过滤器实现

时间:2018-12-12 14:30:37

标签: java list lambda java-8 java-stream

我有需要的情况

  • map一个对象,如果映射函数抛出异常,则将其映射到null
  • filter对象null的映射流,如果为null,则抛出Exception,否则收集到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);

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

5 个答案:

答案 0 :(得分: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。在其他情况下,声明您自己的未经检查的异常类型可能更简洁,以确保不会与应用程序其他组件抛出的其他异常混淆。

答案 1 :(得分: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集合不为空,则可以引发该异常。

答案 2 :(得分:3)

嗯,可以在lambda内使用try-catch子句,但是不建议这样做,因为lambda的长度应尽可能短。

将映射器分隔为新方法,然后在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();
}

答案 3 :(得分: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()中引发任何异常。例如,您可以返回Optional的列表,因此处理流中的空情况很简单。

答案 4 :(得分: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 */}