Java 8 lambda从列表中获取和删除元素

Mar*_*zzi 65 java lambda java-8 java-stream

鉴于元素的列表,我想用一个给定的属性来获取元素,并从列表中删除.我找到的最佳解决方案是:

ProducerDTO p = producersProcedureActive
                .stream()
                .filter(producer -> producer.getPod().equals(pod))
                .findFirst()
                .get();
producersProcedureActive.remove(p);
Run Code Online (Sandbox Code Playgroud)

是否可以在lambda表达式中组合get和remove?

小智 104

要从列表中删除元素

objectA.removeIf(x - > conditions);

例如:objectA.removeIf(x - > blockedWorkerIds.contains(x));

objectA.removeIf(x -> conditions);
Run Code Online (Sandbox Code Playgroud)

输出: A B C.

  • 这并没有回答这个问题。要求是从列表中删除元素,并将删除的项目/项目添加到新列表中。 (8认同)
  • 恕我直言,答案不考虑"获取"部分:`removeIf`是一个优雅的解决方案,从集合中删除元素,但它不会返回被删除的元素. (6认同)

asi*_*d88 29

虽然线程很老,但仍然认为提供解决方案 - 使用Java8.

使用removeIf功能.时间复杂性是O(n)

producersProcedureActive.removeIf(producer -> producer.getPod().equals(pod));
Run Code Online (Sandbox Code Playgroud)

API参考:removeIf docs

假设:producersProcedureActive是一个List

注意:使用此方法,您将无法保留已删除的项目.

  • 除了从列表中删除元素之外,OP 仍然需要对该元素的引用。 (2认同)
  • 只是要注意,这将删除所有符合条件的项目。但OP似乎只需要删除第一项(OP使用findFirst()) (2认同)

Vas*_*sky 15

考虑使用vanilla java迭代器来执行任务:

public static <T> T findAndRemoveFirst(Iterable<? extends T> collection, Predicate<? super T> test) {
    T value = null;
    for (Iterator<? extends T> it = collection.iterator(); it.hasNext();)
        if (test.test(value = it.next())) {
            it.remove();
            return value;
        }
    return null;
}
Run Code Online (Sandbox Code Playgroud)

优点:

  1. 这显而易见.
  2. 它只遍历一次,最多只到匹配元素.
  3. Iterable甚至可以在没有stream()支持的情况下完成它(至少那些remove()在它们的迭代器上实现).

缺点:

  1. 您不能将其作为单个表达式(需要辅助方法或变量)来实现

至于

是否可以在lambda表达式中组合get和remove?

其他答案清楚地表明它是可能的,但你应该知道

  1. 搜索和删除可能会遍历列表两次
  2. ConcurrentModificationException 从正在迭代的列表中删除元素时可能会抛出

  • 你可以假设,但是看了几百个迭代器实现,这将是一个糟糕的假设.(我仍然喜欢这种方法;你只是过度销售它.) (5认同)
  • 我喜欢这个解决方案,但请注意它有一个你错过的严重缺点:许多Iterable实现都有`remove()`抛出UOE的方法.(当然,不是JDK集合的集合,但我认为说"适用于任何Iterable"是不公平的.) (4认同)
  • @Brian Goetz:`removeIf`的`default`实现做了同样的假设,当然,它是在`Collection`而不是`Iterable'上定义的...... (2认同)

Tun*_*aki 10

直接的解决方案是调用ifPresent(consumer)Optional返回的findFirst().当optional不为空时,将调用此使用者.好处还在于,如果find操作返回一个空的可选项,它将不会抛出异常,就像您当前的代码一样; 相反,什么都不会发生.

如果你想返回删除值,你可以mapOptional调用的结果remove:

producersProcedureActive.stream()
                        .filter(producer -> producer.getPod().equals(pod))
                        .findFirst()
                        .map(p -> {
                            producersProcedureActive.remove(p);
                            return p;
                        });
Run Code Online (Sandbox Code Playgroud)

但请注意,该remove(Object)操作将再次遍历列表以查找要删除的元素.如果你有一个随机访问列表,比如a ArrayList,最好在列表的索引上创建一个Stream,并找到与谓词匹配的第一个索引:

IntStream.range(0, producersProcedureActive.size())
         .filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
         .boxed()
         .findFirst()
         .map(i -> producersProcedureActive.remove((int) i));
Run Code Online (Sandbox Code Playgroud)

使用此解决方案,remove(int)操作直接在索引上运行.

  • 这是链表的病态. (2认同)
  • 哦,这么多编辑......现在第一个解决方案没有提供被删除的元素,因为`remove(Object)`只返回一个`boolean`,告诉你是否有一个要删除的元素. (2认同)
  • @Marco Stramezzi:不幸的是,解释它的评论被删除了.如果没有`boxed()`,你会得到一个`OptionalInt`,它只能从`int``映射`到`int`.与`IntStream`不同,没有`mapToObj`方法.使用`boxed()`,你将获得一个`Optional <Integer>`,它允许`map`到一个任意对象,即`remove(int)`返回的`ProducerDTO`.从`Integer`到`int`的强制转换对于消除`remove(int)`和`remove(Object)`之间的歧义是必要的. (2认同)

Toi*_*yen 7

使用可以使用Java 8的过滤器,如果您不想更改旧列表,则创建另一个列表:

List<ProducerDTO> result = producersProcedureActive
                            .stream()
                            .filter(producer -> producer.getPod().equals(pod))
                            .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)


Boh*_*ian 5

我确信这将是一个不受欢迎的答案,但它有效......

ProducerDTO[] p = new ProducerDTO[1];
producersProcedureActive
            .stream()
            .filter(producer -> producer.getPod().equals(pod))
            .findFirst()
            .ifPresent(producer -> {producersProcedureActive.remove(producer); p[0] = producer;}
Run Code Online (Sandbox Code Playgroud)

p[0] 将保存找到的元素或为空。

这里的“技巧”是通过使用有效最终的数组引用来规避“有效最终”问题,但设置其第一个元素。