java流找到匹配还是最后一个?

金潇泽*_*金潇泽 21 java lambda list java-8 java-stream

如何使用java流查找列表中的第一个匹配项或最后一个元素?

这意味着如果没有元素匹配条件,则返回最后一个元素.

例如:

OptionalInt i = IntStream.rangeClosed(1,5)
                         .filter(x-> x == 7)
                         .findFirst();
System.out.print(i.getAsInt());
Run Code Online (Sandbox Code Playgroud)

我该怎么办才能让它返回5;

Nic*_*s K 14

鉴于清单

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Run Code Online (Sandbox Code Playgroud)

你可以这样做:

int value = list.stream().filter(x -> x == 2)
                         .findFirst()
                         .orElse(list.get(list.size() - 1));
Run Code Online (Sandbox Code Playgroud)

这里,如果过滤器的计算结果为true,则检索该元素,否则返回最后一个元素.

如果列表为,则可以返回默认值,例如-1.

int value = list.stream().filter(x -> x == 2)
                         .findFirst()
                         .orElse(list.isEmpty() ? -1 : list.get(list.size() - 1));
Run Code Online (Sandbox Code Playgroud)


sta*_*tut 7

你可以使用这样的reduce()功能:

OptionalInt i = IntStream.rangeClosed(1, 5)
        .reduce((first, second) -> first == 7 ? first : second);
System.out.print(i.getAsInt());
Run Code Online (Sandbox Code Playgroud)

  • @daniu唯一的问题是这不是短路,所以如果你的第一个匹配恰好在第一个元素上,你仍然必须遍历整个流的源,即使你已经知道了结果. (14认同)
  • `reduce`不是为了执行搜索,而是用于关联操作,即元素的总和.`Stream`中有一些叫做`findAny`和`findFirst`的方法可以做到这一点.此外,如果流是并行的,这将不起作用,而`findFirst`将完成确切的工作,尽管流的特征.正如其他人所说,这也不会短路...... (5认同)
  • @FedericoPeraltaSchaffner这个函数是关联的,因此并行执行没有任何问题.所以唯一的问题是效率低下. (4认同)

Rol*_*and 5

基本上我会使用以下两种方法之一或其偏差:

流变体:

<T> T getFirstMatchOrLast(List<T> list, Predicate<T> filter, T defaultValue) {
    return list.stream()
            .filter(filter)
            .findFirst()
            .orElse(list.isEmpty() ? defaultValue : list.get(list.size() - 1));
}
Run Code Online (Sandbox Code Playgroud)

非流式变体:

<T> T getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter, T defaultValue) {
    T relevant = defaultValue;
    for (T entry : iterable) {
        relevant = entry;
        if (filter.test(entry))
            break;
    }
    return relevant;
}
Run Code Online (Sandbox Code Playgroud)

或者正如Ilmari Karonen在评论中建议的那样,Iterable<T>你甚至可以打电话stream::iterator,以防你真的处理一个Stream而不是一个List.调用显示的方法如下所示:

getFirstMatchOrLast(Arrays.asList(1, 20, 3), i -> i == 20, 1); // returns 20
getFirstMatchOrLast(Collections.emptyList(), i -> i == 3, 20); // returns 20
getFirstMatchOrLast(Arrays.asList(1, 2, 20), i -> i == 7, 30); // returns 20
// only non-stream variant: having a Stream<Integer> stream = Stream.of(1, 2, 20)
getFirstMatchOrLast(stream::iterator, i -> i == 7, 30); // returns 20
Run Code Online (Sandbox Code Playgroud)

我不会reduce在这里使用,因为在某种意义上我听起来不对,即使第一个条目已经匹配,它也会遍历整个条目,即它不再短路.而且对我来说它不像filter.findFirst.orElse...... 那么可读(但这可能只是我的意见)

我可能会最终得到如下内容:

<T> Optional<T> getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter) {
    T relevant = null;
    for (T entry : iterable) {
        relevant = entry;
        if (filter.test(entry))
            break;
    }
    return Optional.ofNullable(relevant);
}
// or transform the stream variant to somethinng like that... however I think that isn't as readable anymore...
Run Code Online (Sandbox Code Playgroud)

所以电话看起来像:

getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseThrow(...)
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElse(0);
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseGet(() -> /* complex formula */);
getFirstMatchOrLast(stream::iterator, i -> i == 5).ifPresent(...)
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢你的"非流变体"(有或没有可选),但我建议将它概括为接受任何`Iterable <T>`而不仅仅是`List <T>`.这样,如果你实际上_need_来处理一个流,[你可以将`stream :: iterator`传递给这个方法.](https://ideone.com/4McmOL) (2认同)