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



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



здесь 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 на каждом элементе, который, скорее всего, не вы вообще хотите, как это может ухудшить производительность.
616   7  

7 ответов:

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

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

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

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

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

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


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

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

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

Iterables.getLast(myIterable)

как удобно, проявляющаяся.

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

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

поскольку неясно, является ли это частью спецификации API для reduce чтобы подчиниться приказу встречи, как насчет:

CArea last = data.careas.stream()
    .filter(c -> c.bbox.orientationHorizontal)
    .max((e1, e2) -> data.careas.indexOf(e1) - data.careas.indexOf(e2)).get();

вы также можете использовать функцию skip (), как показано ниже...

long count = data.careas.count();
CArea last = data.careas.stream().skip(count - 1).findFirst().get();

это очень простой в использовании.

Как уже упоминалось @nosid, это можно сделать с помощью reduce. Другой способ сделать это было бы обратить поток, а затем получить первый элемент

CArea last = data.careas.stream()
.filter(c -> c.bbox.orientationHorizontal)
.sorted((a, b)-> -1)
.findFirst().get();

можно использовать java.util.Collections используя следующий статический метод:

Collections.max(yourList);

из документов:

возвращает максимальный элемент данного набора, согласно * естественный заказ ее элементов. [...]

Comments

    Ничего не найдено.