- For the provided input
[1,2,3,4,5,10,12,30,40,50]
, the expected output [1,2,3,4,5,10]
should be returned by simple filter:
static List<Integer> findBelow(List<Integer> inputList, Predicate<Integer> condition) {
return inputList.stream()
.filter(condition::test)
.collect(Collectors.toList());
}
- If a range of indexes needs to be specified, this should be implemented using
IntStream::range
:
static List<Integer> findBelow(List<Integer> inputList, int start, int end, Predicate<Integer> condition) {
start = Math.max(0, start);
end = Math.min(inputList.size(), end);
return IntStream.range(start, end)
.filter(i -> condition.test(inputList.get(i)))
.mapToObj(inputList::get)
.collect(Collectors.toList());
}
Obviously, these implementations do not break
out of the loop, and Java 8 Stream API does not have facilities for that.
The following workarounds can be offered:
- Use
findFirst
to detect the index of the first non-match and then limit the range of index:
static List<Integer> findWithFirst(List<Integer> inputList, int start, int end, Predicate<Integer> condition) {
start = Math.max(0, start);
end = Math.min(inputList.size(), end);
int firstNonMatch = IntStream.range(start, end)
.filter(i -> !condition.test(inputList.get(i)))
.findFirst()
.orElse(maxSize);
return IntStream.range(start, firstNonMatch) // no filter needed any longer
.mapToObj(inputList::get)
.collect(Collectors.toList());
}
- Use boolean flag outside the stream to check if the inverted
break
condition is met while filtering the input list:
// using AtomicBoolean
static List<Integer> findAndBreak2(List<Integer> inputList, int start, int end, Predicate<Integer> condition) {
AtomicBoolean breakFound = new AtomicBoolean(false);
return IntStream.range(start, Math.min(inputList.size(), end))
.filter(i -> {
breakFound.set(
breakFound.get() || !condition.test(inputList.get(i))
);
return !breakFound.get();
})
.mapToObj(inputList::get)
.collect(Collectors.toList());
}
// using static class field
static boolean found = false;
static List<Integer> findAndBreak(List<Integer> inputList, int start, int end, Predicate<Integer> condition) {
found = false;
return IntStream.range(start, Math.min(inputList.size(), end))
.filter(i -> { found |= !condition.test(inputList.get(i)); return !found; })
.mapToObj(inputList::get)
.collect(Collectors.toList());
}
Tests:
Condition i <= 10
is trivial, as it works for all cases.
Invert condition i > 10
as in the OP code demonstrates break
producing empty lists.
Predicate<Integer> condition = (i) -> i > 10;
List<Integer> inputList = Arrays.asList(1,2,3,4,5,10,12,30,40,50);
List<Integer> finalResult = new ArrayList<>();
int index = 0, maxSize = 10;
while (index < maxSize) {
if (condition.test(inputList.get(index))) {
finalResult.add(inputList.get(index));
} else {
break;
}
index++;
}
System.out.println("loop: " + finalResult);
System.out.println("plain filter: " + findBelow(inputList, condition));
System.out.println("filter and range limit: " + findBelow(inputList, 0, maxSize, condition));
System.out.println("filter and break static: " + findAndBreak(inputList, 0, maxSize, condition));
System.out.println("filter and break atomic: " + findAndBreak2(inputList, 0, maxSize, condition));
System.out.println("filter and findFirst: " + findWithFirst(inputList, 0, maxSize, condition));
Output:
loop: []
plain filter: [12, 30, 40, 50]
filter and range limit: [12, 30, 40, 50]
filter and break static: []
filter and break atomic: []
filter and findFirst: []