如何使用 Java Stream API 查找列表中的第一个匹配项或最后一个元素?
这意味着如果没有元素与条件匹配,则应返回最后一个元素。
例如:
OptionalInt i = IntStream.rangeClosed(1,5)
.filter(x-> x == 7)
.findFirst();
System.out.print(i.getAsInt());
我该怎么做才能让它返回5?
给定列表
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));
您可以像这样使用
reduce()
功能:
OptionalInt i = IntStream.rangeClosed(1, 5)
.reduce((first, second) -> first == 7 ? first : second);
System.out.print(i.getAsInt());
基本上我会使用以下两种方法之一或其偏差:
流变体:
<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...
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));
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);
}
功能的短路解决方案:
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);
}
}));
}
方法用于对结果流的第一个元素进行短路检索(如果存在则为匹配元素,否则为最后一个元素)。