如何进行过滤和映射而不会产生重复操作的开销

Pab*_*mez 3 java performance java-stream

我有一些情况下使用Java 8 Stream让我重复执行某些操作,如果没有Stream就可以避免,但我认为问题不在于流,而在于我.

一些例子:

private class Item {
    String id;
    List<String> strings;
}

// This method, filters only the Items that have the strToFind, and
// then maps it to a new string, that has the id and the str found
private void doIt(List<Item> items, String strToFind) {
    items.stream().filter(item -> {
        return item.strings.stream().anyMatch(str -> this.operation(str, strToFind));
    }).map(item -> {
        return item.id + "-" + item.strings.stream()
            .filter(str -> this.operation(str, strToFind)).findAny().get();
    });
}

// This operation can have a lot of overhead, therefore
// it would be really bad to apply it twice
private boolean operation(String str, String strToFind) {
    return str.equals(strToFind);
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,operation每个项目都会调用两次函数,我不希望这样.我首先想到的是直接映射并返回"null"如果没有找到然后过滤空值,但如果我这样做,我将失去对Item的引用,因此,不能使用id.

Jer*_*and 5

我想你可能想要这种行为:

items.stream().map(item -> {
        Optional<String> optional = item.strings.stream().filter(string -> operation(string, strToFind)).findAny();
        if(optional.isPresent()){
            return item.id + "-" + optional.get();
        }
        return null;
    }).filter(e -> e != null);
Run Code Online (Sandbox Code Playgroud)

编辑:因为您在之后进行地图时丢失了过滤器中获得的信息,但没有任何东西阻止您仅在地图中执行操作并在之后进行过滤.

编辑2:正如@Jorn Vernee指出的那样,你可以进一步缩短它:

private void doIt(List<Item> items, String strToFind) {
    items.stream().map(item -> item.strings.stream().filter(string -> operation(string, strToFind)).findAny()
            .map(found -> item.id + "-" + found).orElse(null)).filter(e -> e != null);
}
Run Code Online (Sandbox Code Playgroud)