ChatGPT解决这个技术问题 Extra ChatGPT

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

我正在使用 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 可以直接由第三行赋值?

如果您以后不需要 sourceLongList,那么为了方便起见,可以使用 Collection.removeIf(…)
这个怎么样? List<Long> targetLongList = sourceLongList.stream().collect(Collectors.toList());

M
Maurice Naftalin

您所做的可能是最简单的方法,前提是您的流保持顺序 - 否则您必须在 forEach 之前调用 sequence()。

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

另一种选择是

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

另外:如果您使用 toList 的静态导入,我认为这段代码会变得更短、更清晰、更漂亮。这是通过在文件的导入中放置以下内容来完成的:static import java.util.stream.Collectors.toList;。然后对方付费电话仅读取 .collect(toList())
在 Eclipse 中,可以让 IDE 为方法添加静态导入。这是通过在 Preferences -> 中添加 Collectors 类来完成的。 Java -> 编辑器 -> 内容辅助 -> 收藏夹。在此之后,您只需在点击 Ctr+Space 处键入 toLi 即可让 IDE 填写 toList 并添加静态导入。
要记住的一件事是,IntStream 和其他一些几乎但不完全的Stream 没有 collect(Collector) 方法,您必须调用 IntStream.boxed() 才能将它们转换为常规 { 2} 首先。再说一次,也许你只想要 toArray()
为什么我们必须在 forEach 之前使用 sequential() 或使用“forEachOrdered”
@amarnathharish 因为 forEach 不保证并行流的操作执行顺序。 JavaDoc 说“此操作的行为是明确的不确定性。对于并行流管道,此操作不能保证尊重流的遇到顺序,因为这样做会牺牲并行性的好处。” (这句话的第一句话实际上意味着顺序流也不能保证顺序,尽管实际上它是保留的。)
M
MohamedSanaulla

更新:

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

但是,如果您想要特定的 List 实现,这很有用。
尽管建议针对接口进行编码,但是当您必须针对具体实现进行编码(除非您希望将所有 List 实现编译并作为 javascript 交付)时,仍有明显的情况(其中之一是 GWT)。
此方法的另一个优点,来自 Collectors::toList javadoc:“无法保证返回的 List 的类型、可变性、可序列化性或线程安全性;如果需要对返回的 List 进行更多控制,请使用 toCollection(Supplier)。 "
L
Lii

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

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


如果将 result 声明为 List ,则不需要使用此 util 方法。 Collectors.toList 可以。此外,使用特定的类而不是接口是一种代码味道。
@LluisMartinez:“Collectors.toList 可以。”:不,在很多情况下都不会。因为如果您想稍后在程序中修改列表,则使用 toList 不是一个好主意。 toList documentation 表示:“无法保证返回的 List 的类型、可变性、可序列化性或线程安全性;如果需要对返回的 List 进行更多控制,请使用 toCollection。”。我的回答展示了一种在常见情况下更方便执行此操作的方法。
如果你想专门创建一个 ArrayList 就可以了。
T
Thufir
collect(Collectors.toList());

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

更具体地说:

    List<String> myList = stream.collect(Collectors.toList()); 

从:

https://www.geeksforgeeks.org/collectors-tolist-method-in-java-with-examples/


Z
ZhekaKozlov

Java 16 中有一个新方法 Stream.toList()

List<Long> targetLongList = sourceLongList
         .stream()
         .filter(l -> l > 100)
         .toList();

N
Nikhil Nanivadekar

如果您有一个基元数组,则可以使用 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 的贡献者。


m
msayag

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

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

J
John McClean

如果您不介意使用第 3 方库,AOL 的 cyclops-react 库(披露我是贡献者)具有所有 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)


V
Vaneet Kataria

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

S
Saikat

在可变列表中收集:

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

在不可变列表中收集:

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

JavaDoccollect 的解释:

使用收集器对此流的元素执行可变归约操作。 Collector 封装了用作 collect(Supplier, BiConsumer, BiConsumer) 的参数的函数,允许重用收集策略和组合收集操作,例如多级分组或分区。如果流是并行的,并且收集器是并发的,并且流是无序的或者收集器是无序的,则将执行并发归约(有关并发归约的详细信息,请参见收集器。)这是一个终端操作。当并行执行时,可以实例化、填充和合并多个中间结果,以保持可变数据结构的隔离。因此,即使与非线程安全的数据结构(例如 ArrayList)并行执行,也不需要额外的同步来进行并行归约。


K
Kashyap

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

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

u
user_3380739

这是AbacusUtil的代码

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

披露:我是 AbacusUtil 的开发者。


我在 LongStream 类中找不到任何 toList 方法。你能运行这段代码吗?
@VaneetKataria 在 AbacusUtil 中尝试 com.landawn.abacus.util.stream.LongStreamLongStreamEx
B
B--rian

您可以重写代码如下:

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

感谢您的输入。但是,请解释您所做的更改以及它与问题的相关程度。
在这里,我首先将我的 ArrayList 转换为使用过滤器过滤掉所需的数据。最后,我使用 java 8 流的 collect 方法在称为 targetLongList 的新列表中收集数据。
P
Paul Rooney

如果您不使用 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() 挂钩。终端操作的结果被丢弃。
调用 collect 然后不保存返回值是很奇怪的。在这种情况下,您可以改用 forEach。但这仍然是一个糟糕的解决方案。
以这种方式使用 peek() 是一种反模式。
根据 Stream Java docs peek 方法必须仅用于调试目的。它不应用于除调试以外的任何处理。