从Java 8中的java.util.stream.Stream检索列表


442

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

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可以由第三行直接分配变量?


7
万一您不需要sourceLongList以后,这里便Collection.removeIf(…)为您提供方便。
Holger 2014年

2
这个怎么样?List<Long> targetLongList = sourceLongList.stream().collect(Collectors.toList());
leeyuiwah

Answers:


609

如果您的流保持顺序运行,那么您正在做的事情可能是最简单的方法,否则您将不得不在之前对sequence()进行调用forEach

[后编辑:必须调用sequence()的原因是,forEach(targetLongList::add)如果流是并行的,则表示()的代码将不合理。即使那样,它也不会达到预期的效果,这forEach显然是不确定的-即使在顺序流中,也不能保证元素处理的顺序。您将必须使用forEachOrdered以确保正确的订购。Stream API设计人员的目的是在这种情况下使用收集器,如下所示。]

另一种方法是

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

10
另外:如果您使用的静态导入,我认为此代码会变得更短,更清晰,更漂亮toList。这是通过在文件导入中放置以下内容来完成的:static import java.util.stream.Collectors.toList;。然后,收集呼叫将读取为.collect(toList())
2016年

4
在Eclipse中,可以使IDE为方法添加静态导入。这是通过Collectors“首选项” ->“ Java” ->“ 编辑器” ->“ 内容辅助” ->“ 收藏夹”中添加类来完成的。在此之后,您只需要toLiCtr + Space处输入内容即可输入IDE,toList并添加静态导入。
2016年

3
要记住的一件事是,IntStream其他一些几乎但不是很多Streamcollect(Collector)方法都没有该方法,您必须先调用IntStream.boxed()将它们转换为常规方法Stream。再说一遍,也许你只是想要toArray()
突变鲍勃

为什么我们必须在sequential()之前使用forEach或使用'forEachOrdered`
amarnath harish 18/09/18

1
@amarnathharish因为forEach不保证并行流的操作执行顺序。JavaDoc说:“此操作的行为明确地是不确定的。对于并行流管道,此操作不能保证遵守流的遇到顺序,因为这样做会牺牲并行性的好处。” (此引号的第一句话实际上意味着,尽管实际上保留了顺序流,但也不能保证顺序流的顺序。)
Maurice Naftalin

182

更新:

另一种方法是使用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));

60
但是,如果要特定的List实现,这将很有用。
orbfish

1
尽管建议使用Bee编写针对接口的代码,但是在某些情况下(显然其中一种是GWT),当您不得不针对具体的实现进行编码时(除非您希望所有List实现都以javascript编译和交付)。
爱德华·科伦斯基

3
Collectors::toListjavadoc中提供了该方法的另一个优点:“不能保证返回的List的类型,可变性,可序列化性或线程安全性;如果需要对返回的List进行更多控制,请使用toCollection(Supplier)。”
查尔斯·伍德

12

我喜欢使用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);
}

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


如果将结果声明为List <Long>,则无需使用此util方法。Collectors.toList可以。同样,使用特定的类而不是接口是一种代码味道。
Lluis Martinez

1
@LluisMartinez:“ Collectors.toList可以。” :不,在很多情况下都不是。因为toList例如如果您想稍后在程序中修改列表,则不是一个好主意。该toList文档说:“不能保证返回的List的类型,可变性,可序列化性或线程安全性;如果需要对返回的List进行更多控制,请使用toCollection。” 。我的回答展示了一种在常见情况下可以更方便地执行此操作的方法。
Lii

如果要专门创建一个ArrayList,那就可以了。
Lluis Martinez

5

如果您具有原始数组,则可以使用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 Collections的撰稿人。


4

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

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


1

如果您不介意使用第3方库,则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)


1

LongStream类还有IntStream和DoubleStream类也提供了collect方法的另一个变体。

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

0

如果有人(例如我)正在寻找处理对象而不是原始类型的方法,然后使用 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

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

0

这是AbacusUtil的代码

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

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


我在LongStream类中找不到任何toList方法。您可以运行此代码吗?
Vaneet Kataria,

@VaneetKataria尝试com.landawn.abacus.util.stream.LongStreamLongStreamEx在AbacusUtil中使用
user_3380739

0

您可以如下重写代码:

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

感谢您的输入。但是,请说明您所做的更改以及与问题的关联程度。
B–rian

在这里,我首先将我的ArrayList转换为使用过滤器进行处理,然后过滤掉所需的数据。最后,我使用Java 8流的collect方法在名为targetLongList的新列表中收集数据。
Pratik Pawar

0

收集可变列表:

targetList = sourceList.stream()
                       .filter(i -> i > 100) //apply filter
                       .collect(Collectors.toList());

收集不可变列表:

targetList = sourceList.stream()
                       .filter(i -> i > 100) //apply filter
                       .collect(Collectors.toUnmodifiableList());

collect来自JavaDoc的说明:

使用收集器对此流的元素执行可变还原操作。收集器封装了用作收集参数的函数(供应商,BiConsumer,BiConsumer),从而允许重用收集策略和组成收集操作,例如多级分组或分区。如果流是并行的,并且收集器是并发的,并且流是无序的或收集器是无序的,则将执行并发缩减(请参阅收集器以获取并发缩减的详细信息。)

这是终端操作。

当并行执行时,可以实例化,填充和合并多个中间结果,以保持可变数据结构的隔离。因此,即使与非线程安全数据结构(例如ArrayList)并行执行时,也不需要其他同步来进行并行缩减。


-3

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

我不喜欢collect()仅用于驱动流,以便在每个项目上调用peek()挂钩。终端操作的结果将被丢弃。
Daniel K.

调用collect然后不保存返回值非常奇怪。在这种情况下,您可以forEach改用。但这仍然是一个糟糕的解决方案。
Lii

1
以这种方式使用peek()是一种反模式。
Grzegorz Piwowarek '17年

根据Stream Java docs peek方法必须仅用于调试目的,除调试外,不得用于任何处理。
Vaneet Kataria,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.