Получение списка из java.util.stream.Stream в Java 8

Я играл с Java 8 lambdas, чтобы легко фильтровать коллекции. Но я не нашел краткого способа получить результат как новый список в одном и том же выражении. Вот мой самый краткий подход:

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = new ArrayList<>();
sourceLongList.stream().filter(l -> l > 100).forEach(targetLongList::add);

Примеры в сети не ответили на мой вопрос, потому что они останавливаются, не создавая новый список результатов. Должен быть более сжатый способ. Я бы предположил, что класс Stream имеет методы как toList(), toSet(),...

Есть ли способ, которым переменные targetLongList могут быть напрямую назначены третьей строкой?

Ответ 1

То, что вы делаете, может быть самым простым способом, если ваш поток остается последовательным и в противном случае вам придется поместить вызов в последовательный() до forEach.

[позже изменить: причина, по которой необходим вызов для последовательного(), состоит в том, что код в его начале (forEach(targetLongList::add)) был бы ярким, если бы поток был параллельным. Даже тогда он не достигнет предполагаемого эффекта, так как forEach явно недетерминирован и даже в последовательном потоке порядок обработки элементов не гарантируется. Вы должны использовать forEachOrdered для обеспечения правильного упорядочения. Намерение дизайнеров Stream API состоит в том, что вы будете использовать коллекционер в этой ситуации, как показано ниже.]

Альтернативой является

targetLongList = sourceLongList.stream()
    .filter(l -> l > 100)
    .collect(Collectors.toList());

Ответ 2

Обновлено:

Другой подход заключается в использовании Collectors.toList:

targetLongList = 
    sourceLongList.stream().
    filter(l -> l > 100).
    collect(Collectors.toList());

Предыдущее решение:

Другой подход заключается в использовании Collectors.toCollection:

targetLongList = 
    sourceLongList.stream().
    filter(l -> l > 100).
    collect(Collectors.toCollection(ArrayList::new));

Ответ 3

Мне нравится использовать метод util, который возвращает коллекционер для ArrayList, когда это то, что я хочу.

Я думаю, что решение, использующее Collectors.toCollection(ArrayList::new), слишком шумно для такой общей операции.

Пример:

ArrayList<Long> result = sourceLongList.stream()
    .filter(l -> l > 100)
    .collect(toArrayList());

public static <T> Collector<T, ?, ArrayList<T>> toArrayList() {
    return Collectors.toCollection(ArrayList::new);
}

С этим ответом я также хочу продемонстрировать, насколько просто создавать и использовать пользовательские коллекторы, что очень полезно в целом.

Ответ 4

Если у вас есть массив примитивов, вы можете использовать примитивные коллекции, доступные в Eclipse Collections.

LongList sourceLongList = LongLists.mutable.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
LongList targetLongList = sourceLongList.select(l -> l > 100);

Если вы не можете изменить sourceLongList с List:

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = 
    ListAdapter.adapt(sourceLongList).select(l -> l > 100, new ArrayList<>());

Если вы хотите использовать LongStream:

long[] sourceLongs = new long[]{1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L};
LongList targetList = 
    LongStream.of(sourceLongs)
    .filter(l -> l > 100)
    .collect(LongArrayList::new, LongArrayList::add, LongArrayList::addAll);

Примечание. Я участвую в коллекциях Eclipse.

Ответ 5

Немного более эффективный способ (не создавайте исходный список и автоматическую распаковку фильтра):

List<Long> targetLongList = LongStream.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L)
    .filter(l -> l > 100)
    .boxed()
    .collect(Collectors.toList());

Ответ 6

collect(Collectors.toList());

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

Ответ 7

Если вы не возражаете против использования сторонних библиотек, AOL cyclops-react lib (раскрытие я являюсь вкладчиком) имеет расширения для всех JDK Collection, включая List. Интерфейс ListX расширяет java.util.List и добавляет большое количество полезных операторов, включая фильтр.

Вы можете просто написать -

ListX<Long> sourceLongList = ListX.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
ListX<Long> targetLongList = sourceLongList.filter(l -> l > 100);

ListX также может быть создан из существующего списка (через ListX.fromIterable)

Ответ 8

Существует еще один вариант метода сбора данных, предоставляемый классом LongStream, а также классами IntStream и DoubleStream.

<R> R collect(Supplier<R> supplier,
              ObjLongConsumer<R> accumulator,
              BiConsumer<R,R> combiner)

Выполняет изменяемую операцию сокращения над элементами этого потока. Изменяемое сокращение - это такое, в котором уменьшенное значение представляет собой изменяемый контейнер результатов, такой как ArrayList, и элементы включаются путем обновления состояния результата, а не путем замены результата. Это дает результат, эквивалентный:

R result = supplier.get();
  for (long element : this stream)
       accumulator.accept(result, element);
  return result;

Как и Reduce (long, LongBinaryOperator), операции сбора могут быть распараллелены без дополнительной синхронизации. Это терминальная операция.

И ответ на ваш вопрос с этим методом сбора, как показано ниже:

    LongStream.of(1L, 2L, 3L, 3L).filter(i -> i > 2)
    .collect(ArrayList::new, (list, value) -> list.add(value)
    , (list1, list2) -> list1.addAll(list2));

Ниже приведен справочный вариант метода, который довольно умен, но кое-что сложно понять:

     LongStream.of(1L, 2L, 3L, 3L).filter(i -> i > 2)
    .collect(ArrayList::new, List::add , List::addAll);

Ниже будет вариант HashSet:

     LongStream.of(1L, 2L, 3L, 3).filter(i -> i > 2)
     .collect(HashSet::new, HashSet::add, HashSet::addAll);

Аналогично вариант LinkedList выглядит так:

     LongStream.of(1L, 2L, 3L, 3L)
     .filter(i -> i > 2)
     .collect(LinkedList::new, LinkedList::add, LinkedList::addAll);

Ответ 9

В случае, если кто-то (например, я) ищет способы использования объектов вместо примитивных типов, используйте mapToObj()

String ss = "An alternative way is to insert the following VM option before "
        + "the -vmargs option in the Eclipse shortcut properties(edit the "
        + "field Target inside the Shortcut tab):";

List<Character> ll = ss
                        .chars()
                        .mapToObj(c -> new Character((char) c))
                        .collect(Collectors.toList());

System.out.println("List type: " + ll.getClass());
System.out.println("Elem type: " + ll.get(0).getClass());
ll.stream().limit(50).forEach(System.out::print);

печатает:

List type: class java.util.ArrayList
Elem type: class java.lang.Character
An alternative way is to insert the following VM o

Ответ 10

Вот код от AbacusUtil

LongStream.of(1, 10, 50, 80, 100, 120, 133, 333).filter(e -> e > 100).toList();

Раскрытие информации: я разработчик AbacusUtil.

Ответ 11

String joined = 
                Stream.of(isRead?"read":"", isFlagged?"flagged":"", isActionRequired?"action":"", isHide?"hide":"")
                      .filter(s -> s != null && !s.isEmpty())
                      .collect(Collectors.joining(","));

Ответ 12

Вы можете переписать код, как показано ниже:

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = sourceLongList.stream().filter(l -> l > 100).collect(Collectors.toList());

Ответ 13

Если вы не используете parallel(), это будет работать

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);

List<Long> targetLongList =  new ArrayList<Long>();

sourceLongList.stream().peek(i->targetLongList.add(i)).collect(Collectors.toList());