我一直在使用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);
}
有了这个答案,我还想演示创建和使用自定义收集器是多么简单,这通常是非常有用的。
在一个可变列表中收集:
targetList = sourceList.stream()
.filter(i -> i > 100) //apply filter
.collect(Collectors.toList());
在一个不可变列表中收集:
targetList = sourceList.stream()
.filter(i -> i > 100) //apply filter
.collect(Collectors.toUnmodifiableList());
从JavaDoc中collect的解释:
Performs a mutable reduction operation on the elements of this stream
using a Collector. A Collector encapsulates the functions used as
arguments to collect(Supplier, BiConsumer, BiConsumer), allowing for
reuse of collection strategies and composition of collect operations
such as multiple-level grouping or partitioning. If the stream is
parallel, and the Collector is concurrent, and either the stream is
unordered or the collector is unordered, then a concurrent reduction
will be performed (see Collector for details on concurrent reduction.)
This is a terminal operation.
When executed in parallel, multiple intermediate results may be
instantiated, populated, and merged so as to maintain isolation of
mutable data structures. Therefore, even when executed in parallel
with non-thread-safe data structures (such as ArrayList), no
additional synchronization is needed for a parallel reduction.
如果你不介意使用第三方库,AOL的cyclops-react库(披露我是一个贡献者)有所有JDK集合类型的扩展,包括列表。ListX接口扩展了java.util.List,并添加了大量有用的操作符,包括filter。
你可以简单地写成-
ListX<Long> sourceLongList = ListX.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
ListX<Long> targetLongList = sourceLongList.filter(l -> l > 100);
ListX也可以从现有的List中创建(通过ListX. fromitable)
您所做的可能是最简单的方法,前提是您的流保持顺序—否则您将不得不在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());
如果您有一个原语数组,您可以使用Eclipse collections中提供的原语集合。
LongList sourceLongList = LongLists.mutable.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
LongList targetLongList = sourceLongList.select(l -> l > 100);
如果你不能从List中更改sourceLongList:
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 Collections的贡献者。