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

时间:2016-02-29 13:52:46

标签: java lambda java-8 java-stream

给定一个元素列表,我想获得具有给定属性的元素从列表中删除它。我找到的最佳解决方案是:

ProducerDTO p = producersProcedureActive
                .stream()
                .filter(producer -> producer.getPod().equals(pod))
                .findFirst()
                .get();
producersProcedureActive.remove(p);

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

12 个答案:

答案 0 :(得分:88)

要从列表中删除元素

objectA.removeIf(x - > conditions);

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

List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");

List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");

str1.removeIf(x -> str2.contains(x)); 

str1.forEach(System.out::println);

<强>输出: 一个 乙 ç

答案 1 :(得分:24)

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

使用removeIf功能。时间复杂度为O(n)

producersProcedureActive.removeIf(producer -> producer.getPod().equals(pod));

API参考:removeIf docs

假设:producersProcedureActiveList

注意:使用这种方法,您无法获得已删除项目的保留。

答案 2 :(得分:15)

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

ACTION_VIEW

<强>优点

  1. 显而易见。
  2. 它只遍历一次,最多只到匹配元素。
  3. 即使没有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; } 支持(至少在其迭代器上实现Iterable ),您也可以在任何stream()上执行此操作。
  4. <强>缺点

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

        

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

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

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

答案 3 :(得分:9)

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

如果您想要返回已移除的值,可以map Optional调用remove的结果:

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

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

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

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

答案 4 :(得分:5)

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

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

答案 5 :(得分:3)

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

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

p[0]将保留找到的元素或为null。

这里的“技巧”是通过使用数组引用来避开“有效最终”问题,该引用实际上是最终的,但设置了它的第一个元素。

答案 6 :(得分:2)

使用Eclipse Collections,您可以在任何java.util.List上使用detectIndexremove(int)

List<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int index = Iterate.detectIndex(integers, i -> i > 2);
if (index > -1) {
    integers.remove(index);
}

Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);

如果您使用Eclipse Collections中的MutableList类型,则可以直接在列表中调用detectIndex方法。

MutableList<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int index = integers.detectIndex(i -> i > 2);
if (index > -1) {
    integers.remove(index);
}

Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);

注意:我是Eclipse Collections的提交者

答案 7 :(得分:1)

正如其他人所建议的那样,这可能是循环和迭代的用例。在我看来,这是最简单的方法。如果您想要就地修改列表,则不能将其视为真实的&#34;无论如何功能编程。但是你可以使用Collectors.partitioningBy()来获得一个包含满足你条件的元素的新列表,以及一个新的列表。当然,使用这种方法,如果您有多个元素满足条件,那么所有元素都将在该列表中,而不仅仅是第一个元素。

答案 8 :(得分:0)

结合我最初的想法和你的答案,我达到了似乎是解决方案 我自己的问题:

<LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:orientation="horizontal">

   <ImageView
     android:id="@+id/imgMoviePoster"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:layout_marginBottom="10dp"
     android:scaleType="centerCrop"/>
</LinearLayout>
<LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:orientation="horizontal">

    <ListView
      android:id="@+id/lstMovies"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"/>
</LinearLayout>

它允许使用不再遍历的public ProducerDTO findAndRemove(String pod) { ProducerDTO p = null; try { p = IntStream.range(0, producersProcedureActive.size()) .filter(i -> producersProcedureActive.get(i).getPod().equals(pod)) .boxed() .findFirst() .map(i -> producersProcedureActive.remove((int)i)) .get(); logger.debug(p); } catch (NoSuchElementException e) { logger.error("No producer found with POD [" + pod + "]"); } return p; } 删除对象 列表(由@Tunaki建议)它允许将删除的对象返回到 函数调用者。

我看了你的答案,建议我选择remove(int)而不是ifPresent等安全方法,但我找不到在这种情况下使用它们的方法。

这种解决方案有什么重要的缺点吗?

编辑以下@Holger建议

这应该是我需要的功能

get

答案 9 :(得分:0)

以下逻辑是不修改原始列表的解决方案

List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");

List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");

List<String> str3 = str1.stream()
                        .filter(item -> !str2.contains(item))
                        .collect(Collectors.toList());

str1 // ["A", "B", "C", "D"]
str2 // ["D", "E"]
str3 // ["A", "B", "C"]

答案 10 :(得分:0)

当我们想将列表中的多个元素放入新列表(使用谓词进行过滤)并将其从现有列表中删除时,我在任何地方都找不到合适的答案。

这是我们使用Java Streaming API分区的方法。

Map<Boolean, List<ProducerDTO>> classifiedElements = producersProcedureActive
    .stream()
    .collect(Collectors.partitioningBy(producer -> producer.getPod().equals(pod)));

// get two new lists 
List<ProducerDTO> matching = classifiedElements.get(true);
List<ProducerDTO> nonMatching = classifiedElements.get(false);

// OR get non-matching elements to the existing list
producersProcedureActive = classifiedElements.get(false);

这样,您可以有效地从原始列表中删除过滤的元素,并将其添加到新列表中。

请参考 5.2。 this article的Collectors.partitioningBy 部分。

答案 11 :(得分:0)

任务是:获取✶✶从列表中删除元素

p.stream().collect( Collectors.collectingAndThen( Collector.of(
    ArrayDeque::new,
    (a, producer) -> {
      if( producer.getPod().equals( pod ) )
        a.addLast( producer );
    },
    (a1, a2) -> {
      return( a1 );
    },
    rslt -> rslt.pollFirst()
  ),
  (e) -> {
    if( e != null )
      p.remove( e );  // remove
    return( e );    // get
  } ) );