it-swarm.com.ru

Получить последний элемент Stream / List в одну строку

Как я могу получить последний элемент потока или списка в следующем коде?

Где data.careas - это List<CArea>:

CArea first = data.careas.stream()
                  .filter(c -> c.bbox.orientationHorizontal).findFirst().get();

CArea last = data.careas.stream()
                 .filter(c -> c.bbox.orientationHorizontal)
                 .collect(Collectors.toList()).; //how to?

Как видите, получить первый элемент с определенным filter несложно.

Однако получить последний элемент в одной строке - настоящая боль:

  • Кажется, я не могу получить его напрямую от Stream. (Это будет иметь смысл только для конечных потоков)
  • Также кажется, что вы не можете получить такие вещи, как first() и last() из интерфейса List, что действительно является проблемой.

Я не вижу никаких аргументов для того, чтобы не предоставлять методы first() и last() в интерфейсе List, так как элементы там упорядочены, и, кроме того, размер известен.

Но согласно первоначальному ответу: как получить последний элемент конечного Stream?

Лично это самое близкое, что я мог получить:

int lastIndex = data.careas.stream()
        .filter(c -> c.bbox.orientationHorizontal)
        .mapToInt(c -> data.careas.indexOf(c)).max().getAsInt();
CArea last = data.careas.get(lastIndex);

Однако это подразумевает использование indexOf для каждого элемента, что, скорее всего, нежелательно, поскольку это может ухудшить производительность.

90
skiwi

Можно получить последний элемент с помощью метода Stream :: red . Следующий листинг содержит минимальный пример для общего случая:

Stream<T> stream = ...; // sequential or parallel stream
Optional<T> last = stream.reduce((first, second) -> second);

Эта реализация работает для всех упорядоченные потоки (включая потоки, созданные из Lists ). Для неупорядоченных потоков по очевидным причинам не указано, какой элемент будет возвращен.

Реализация работает как для последовательных , так и параллельных потоков . На первый взгляд это может удивить, и, к сожалению, в документации это не указано явно. Тем не менее, это важная особенность потоков, и я попытаюсь прояснить это:

  • Javadoc для метода Stream :: red утверждает, что он " не ограничен выполнить последовательно ".
  • Javadoc также требует, чтобы "функция аккумулятора была ассоциативной , не мешающая , функция без сохранения состояния для объединения двух значений ", что, очевидно, имеет место для лямбды выражение (first, second) -> second.
  • Javadoc для операции сокращения состояния: "Классы потоков имеют несколько форм общих операций сокращения, называемых redu () и collect () [..] " и " правильно сконструированная операция сокращения является по своей сути распараллеливаемой До тех пор, пока функции, используемые для обработки элементов, ассоциативно и без состояния . "

Документация для тесно связанных Collectors еще более явна: "Чтобы убедиться, что последовательно и параллельные исполнения дают эквивалентные результаты , функции коллектора должны удовлетворять тождеству и an ассоциативность ограничения. "


Вернемся к исходному вопросу: следующий код хранит ссылку на последний элемент в переменной last и выдает исключение, если поток пуст. Сложность линейна по длине потока.

CArea last = data.careas
                 .stream()
                 .filter(c -> c.bbox.orientationHorizontal)
                 .reduce((first, second) -> second).get();
157
nosid

Если у вас есть коллекция (или, в общем, итеративный), вы можете использовать Google Guava

Iterables.getLast(myIterable)

как удобный вкладыш.

36
Peti

Один лайнер (нет необходимости в потоке;):

Object lastElement = list.get(list.size()-1);
9
nimo23

Гуава выделил метод для этого случая:

Stream<T> stream = ...;
Optional<T> lastItem = Streams.findLast(stream);

Это эквивалентно stream.reduce((a, b) -> b), но создатели утверждают, что он имеет гораздо лучшую производительность.

От документация :

Время выполнения этого метода будет между O (log n) и O (n), лучше работая на эффективно разделяемых потоках.

Стоит отметить, что если поток неупорядочен, этот метод ведет себя как findAny().

1
k13i