使用 Java 流查找匹配元素(如果存在),否则查找最后一个?

问题描述 投票:0回答:6

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

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

例如:

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

我该怎么做才能让它返回5?

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

给定列表

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

你可以这样做:

int value = list.stream().filter(x -> x == 2)
                         .findFirst()
                         .orElse(list.get(list.size() - 1));

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

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

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

7
投票

您可以像这样使用

reduce()
功能:

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

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

非流变体:

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

或者正如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

,因为从某种意义上说,这对我来说听起来是错误的,即使第一个条目可能已经匹配,它也会遍历整个条目,即它不再短路。此外,对我来说,它不如

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...



3
投票

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(...)

但是您最好将生成的数字收集到列表中,然后按如下方式对其进行操作:

int startInc = 1; int endEx = 5; OptionalInt first = IntStream.concat(IntStream.range(startInc, endEx) .filter(x -> x == 7), endEx > 1 ? IntStream.of(endEx) : IntStream.empty()) .findFirst();

或者,如果您想让后者在源为空的情况下返回一个空的Optional(如果这是可能的情况)而不是异常,那么您可以这样做:

// first collect the numbers into a list List<Integer> result = IntStream.rangeClosed(startInc,endEx) .boxed() .collect(toList()); // then operate on it int value = result.stream() .filter(x -> x == 7) .findFirst() .orElse(result.get(result.size() - 1));



2
投票
List<Integer> result = IntStream.rangeClosed(startInc,endEx) .boxed() .collect(toList()); Optional<Integer> first = Stream.concat(result.stream().filter(x -> x == 7), result.isEmpty() ? Stream.empty() : Stream.of(result.get(result.size() - 1))) .findFirst();

循环就足够了:


for

然后可以这样称呼:

public static <T> T getFirstMatchingOrLast(List<? extends T> source, Predicate<? super T> predicate){ // handle empty case if(source.isEmpty()){ return null; } for(T t : source){ if(predicate.test(t)){ return t; } } return source.get(source.size() -1); }



0
投票
Stream Gatherers

功能的短路解决方案: Integer match = getFirstMatchingOrLast(ints, i -> i == 7);

Optional<Integer> i = IntStream.rangeClosed(1,5)
        .boxed()
        .gather(filterPlusLast(x -> x == 7))
        .findFirst();
System.out.print(i.orElseThrow());
这个收集器过滤掉除与谓词匹配的元素之外的所有元素,并且它还保留最后一个元素。  

private static Gatherer<Integer, ?, Integer> filterPlusLast(IntPredicate predicate) { class State { Integer last; } return Gatherer.ofSequential( State::new, Gatherer.Integrator.ofGreedy((state, element, downstream) -> { state.last = element; if (predicate.test(element)) { return downstream.push(element); } else { return true; } }), ((state, downstream) -> { if (state.last != null && !predicate.test(state.last)) { downstream.push(state.last); } })); }

 方法用于对结果流的第一个元素进行短路检索(如果存在则为匹配元素,否则为最后一个元素)。

© www.soinside.com 2019 - 2024. All rights reserved.