我一直在使用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可以直接由第三行分配?


当前回答

我喜欢使用util方法,当我需要时,它会为ArrayList返回一个收集器。

我认为使用collections . 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);
}

有了这个答案,我还想演示创建和使用自定义收集器是多么简单,这通常是非常有用的。

其他回答

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)一样,collect操作可以并行化,而不需要额外的同步。 这是一个终端操作。

用这种收集方法回答你的问题如下:

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

如果你不使用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());

您所做的可能是最简单的方法,前提是您的流保持顺序—否则您将不得不在forEach之前调用sequential()。

[later edit: the reason the call to sequential() is necessary is that the code as it stands (forEach(targetLongList::add)) would be racy if the stream was parallel. Even then, it will not achieve the effect intended, as forEach is explicitly nondeterministic—even in a sequential stream the order of element processing is not guaranteed. You would have to use forEachOrdered to ensure correct ordering. The intention of the Stream API designers is that you will use collector in this situation, as below.]

另一种选择是

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

一个更有效的方法(避免创建源列表和由过滤器自动开箱):

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

这里是常用的算盘代码

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

披露:我是abacus-common的开发者。