Подтвердить что ты не робот

Получить последние n элементов из потока

Мне интересно, есть ли альтернатива

List<X> lastN = all.subList(Math.max(0, all.size() - n), all.size());

с stream использованием?

4b9b3361

Ответ 1

Пользовательский коллектор можно записать так:

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

Ответ 2

Используйте Stream.skip()

Возвращает поток, состоящий из оставшихся элементов этого потока после отбрасывания первых n элементов потока. Если этот поток содержит менее n элементов, будет возвращен пустой поток.

all.stream().skip(Math.max(0, all.size() - n)).forEach(doSomething);

Ответ 3

Если поток имеет неизвестный размер, то, вероятно, нет пути для потребления всего потока и буферизации последних 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).

Ответ 4

Иногда мне нужен "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