关于集合:从Java 8中的java.util.stream.Stream中检索List

Retrieving a List from a java.util.stream.Stream in Java 8

我正在玩Java 8 lambdas来轻松过滤集合。 但我没有找到一种简洁的方法来将结果作为新列表检索到同一语句中。 到目前为止,这是我最简洁的方法:

1
2
3
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


你正在做的事情可能是最简单的方法,只要你的流保持顺序&mdash;否则你将不得不在forEach之前调用sequential()。

[稍后编辑:调用sequential()是必要的原因是,如果流是并行的,代码(forEach(targetLongList::add))将是racy。即使这样,它也不会达到预期的效果,因为forEach明确是非确定性的 - 即使在顺序流中,也不能保证元素处理的顺序。您必须使用forEachOrdered来确保正确的排序。 Stream API设计者的目的是在这种情况下使用收集器,如下所示。

另一种选择是

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


更新:

另一种方法是使用Collectors.toList

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

以前的方案:

另一种方法是使用Collectors.toCollection

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


我喜欢使用一个util方法,当我想要的时候返回ArrayList的收集器。

我认为使用Collectors.toCollection(ArrayList::new)的解决方案对于这种常见操作来说有点太吵了。

例:

1
2
3
4
5
6
7
ArrayList<Long> result = sourceLongList.stream()
    .filter(l -> l > 100)
    .collect(toArrayList());

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

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


如果您有一个基元数组,则可以使用Eclipse Collections中提供的基元集合。

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

如果您无法从List更改sourceLongList:

1
2
3
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

1
2
3
4
5
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的贡献者。


更有效的方法(避免创建源列表和过滤器自动取消装箱):

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

1
collect(Collectors.toList());

这是您可以用来将任何Stream转换为List的调用。


LongStream类提供了另一种collect方法变体
类似地,IntStream和DoubleStream类也是如此。

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

对此流的元素执行可变减少操作。可变减少是其中减少的值是可变结果容器(例如ArrayList),并且通过更新结果的状态而不是通过替换结果来合并元素。这产生的结果相当于:

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

与reduce(long,LongBinaryOperator)一样,可以并行化收集操作,而无需额外的同步。
这是终端操作。

使用此收集方法回答您的问题如下:

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

下面是方法参考变体,它非常聪明,但有些难以理解:

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

下面是HashSet变体:

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

类似LinkedList变体是这样的:

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

如果您不介意使用第三方库,AOL的cyclops-react lib(披露我是贡献者)具有所有JDK Collection类型的扩展,包括List。 ListX接口扩展了java.util.List并添加了大量有用的运算符,包括filter。

你可以简单地写 -

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

ListX也可以从现有List创建(通过ListX.fromIterable)


您可以重写代码如下:

1
2
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());


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

这是AbacusUtil的代码

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

披露:我是AbacusUtil的开发人员。


如果有人(像我一样)在寻找处理Objects而不是原始类型的方法,那么使用mapToObj()

1
2
3
4
5
6
7
8
9
10
11
12
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);

打印:

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

如果你不使用parallel(),这将有效

1
2
3
4
5
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());