Мне интересно, есть ли альтернатива
List<X> lastN = all.subList(Math.max(0, all.size() - n), all.size());
с stream использованием?
Мне интересно, есть ли альтернатива
List<X> lastN = all.subList(Math.max(0, all.size() - n), all.size());
с stream использованием?
Пользовательский коллектор можно записать так:
public static <T> Collector<T, ?, List<T>> lastN(int n) {
return Collector.<T, Deque<T>, List<T>>of(ArrayDeque::new, (acc, t) -> {
if(acc.size() == n)
acc.pollFirst();
acc.add(t);
}, (acc1, acc2) -> {
while(acc2.size() < n && !acc1.isEmpty()) {
acc2.addFirst(acc1.pollLast());
}
return acc2;
}, ArrayList::new);
}
И используйте это так:
List<String> lastTen = input.stream().collect(lastN(10));
Используйте Stream.skip()
Возвращает поток, состоящий из оставшихся элементов этого потока после отбрасывания первых n элементов потока. Если этот поток содержит менее n элементов, будет возвращен пустой поток.
all.stream().skip(Math.max(0, all.size() - n)).forEach(doSomething);
Если поток имеет неизвестный размер, то, вероятно, нет пути для потребления всего потока и буферизации последних n
элементов, встречающихся до сих пор. Вы можете сделать это с помощью какого-то deque или специализированного кольцевого буфера, автоматически сохраняющего максимальный размер (см. этот связанный вопрос для некоторых реализаций).
public static <T> List<T> lastN(Stream<T> stream, int n) {
Deque<T> result = new ArrayDeque<>(n);
stream.forEachOrdered(x -> {
if (result.size() == n) {
result.pop();
}
result.add(x);
});
return new ArrayList<>(result);
}
Все эти операции (size
, pop
, add
) должны иметь сложность O (1), поэтому общая сложность потока с (неизвестной) длиной n будет O (n).
Иногда мне нужен "oneliner" (в данном случае три лайнера), так как создание коллектора - это слишком много хлопот.
Если поток небольшой, то можно снова выполнить reverse
, limit
и reverse
, не жертвуя при этом производительностью. Это приведет к последним n элементам.
Это полезно, если требуется фильтрация, так как в этом случае невозможно указать размер.
Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9)
.filter(i -> i % 2 == 0)
.sorted(Comparator.reverseOrder())
.limit(2)
.sorted(Comparator.reverseOrder())
.forEach(System.out::println); // prints 8 6