Java8Lambda从列表中获取并删除元素

Java8Lambda从列表中获取并删除元素,java,lambda,java-8,java-stream,Java,Lambda,Java 8,Java Stream,给定一个元素列表,我想获取具有给定属性的元素,并将其从列表中删除。我找到的最佳解决方案是: ProducerDTO p = producersProcedureActive .stream() .filter(producer -> producer.getPod().equals(pod)) .findFirst() .get(); producersProce

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

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

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

直接的解决方案是调用由返回的可选函数。当可选项不为空时,将调用此使用者。其好处还在于,如果find操作返回一个空的可选值,它不会抛出异常,就像您当前的代码一样;相反,什么也不会发生

如果要返回删除的值,可以将
可选
映射到调用
删除
的结果:

producersProcedureActive.stream()
                        .filter(producer -> producer.getPod().equals(pod))
                        .findFirst()
                        .map(p -> {
                            producersProcedureActive.remove(p);
                            return p;
                        });
但请注意,该操作将再次遍历列表以查找要删除的元素。如果您有一个随机访问的列表,如
ArrayList
,最好在列表的索引上创建一个流,并找到与谓词匹配的第一个索引:

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

使用此解决方案,操作将直接对索引进行操作。

我确信这将是一个不受欢迎的答案,但它可以

ProducerDTO[] p = new ProducerDTO[1];
producersProcedureActive
            .stream()
            .filter(producer -> producer.getPod().equals(pod))
            .findFirst()
            .ifPresent(producer -> {producersProcedureActive.remove(producer); p[0] = producer;}
p[0]
将保留找到的元素或为空


这里的“诀窍”是通过使用一个实际上是final的数组引用来避免“effectivefinal”问题,但要设置它的第一个元素。

考虑使用普通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;
}
public static T find和removefirst(Iterable),您可以在任何java.util.List上使用
detectIndex
remove(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集合的提交者

正如其他人所建议的,这可能是循环和可重用的一个用例。在我看来,这是最简单的方法。如果您想修改列表,它无论如何都不能被认为是“真正的”函数编程。但是您可以使用
收集器。partitioningBy()
为了得到一个包含满足您条件的元素的新列表,以及一个包含不满足您条件的元素的新列表。当然,通过这种方法,如果您有多个满足条件的元素,那么所有这些元素都将在该列表中,而不仅仅是第一个元素。

结合我最初的想法和您的答案,我得出了一个似乎是解决方案的列表 对于我自己的问题:

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;
}
它允许使用不再遍历对象的
remove(int)
删除对象 list(如@Tunaki所建议的)允许将删除的对象返回到 函数调用者

我阅读了你的答案,其中建议我选择安全的方法,比如
ifPresent
,而不是
get
,但我没有找到在这种情况下使用它们的方法

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

编辑以下@Holger建议

这应该是我需要的功能

public ProducerDTO findAndRemove(String pod) {
    return IntStream.range(0, producersProcedureActive.size())
            .filter(i -> producersProcedureActive.get(i).getPod().equals(pod))      
            .boxed()                                                                
            .findFirst()
            .map(i -> producersProcedureActive.remove((int)i))
            .orElseGet(() -> {
                logger.error("No producer found with POD [" + pod + "]"); 
                return null; 
            });
}

从列表中删除元素

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

objectA.removeIf(x->blockedWorkerIds.contains(x));
List str1=new ArrayList();
str1.添加(“A”);
str1.添加(“B”);
str1.添加(“C”);
str1.添加(“D”);
List str2=new ArrayList();
str2.添加(“D”);
str2.添加(“E”);
str1.removeIf(x->str2.contains(x));
str1.forEach(System.out::println);
输出: A. B
C

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

使用
removeIf
函数。时间复杂度为
O(n)

API参考:

假设:
producerprocedureactive
是一个
列表


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

如果您不想更改旧列表,请使用Java 8的筛选器创建另一个列表:

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

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

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"]
List str1=new ArrayList();
str1.添加(“A”);
str1.添加(“B”);
str1.添加(“C”);
str1.添加(“D”);
List str2=new ArrayList();
str2.添加(“D”);
str2.添加(“E”);
列表str3=str1.stream()
.filter(项目->!str2.contains(项目))
.collect(Collectors.toList());
str1/[“A”、“B”、“C”、“D”]
str2/[“D”,“E”]
str3/[“A”、“B”、“C”]

当我们想从一个列表中获取多个元素到一个新列表中(使用谓词过滤)并将它们从现有列表中删除时,我在任何地方都找不到正确的答案

下面是我们如何使用Java流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);
Map classifiedElements=producerproceductive
.stream()
.collect(collector.partitionby(producer->producer.getPod().equals(pod));
//获取两个新列表
列表匹配=classifiedElements.get(true);
List nonMatching=classifiedElements.get(false);
//或者将不匹配的元素获取到现有列表
producerprocedureactive=classifiedElements.get(false);
通过这种方式,可以有效地从原始列表中删除过滤后的元素,并将其添加到新列表中

请参阅的5.2.Collectors.partitionby部分。

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

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

这对于链表来说是病态的。@chrylis索引解决方案确实是。根据列表实现的不同,一方会选择另一方。进行了一个小编辑。@chrylis:对于
LinkedList
的情况,您可能不应该使用流API,因为没有不至少遍历两次的解决方案。但我不知道有什么解决方案在现实生活中,链表的学术优势可以补偿其实际开销。因此简单的解决方案是永远不要使用
LinkedList
。哦,这么多编辑…现在第一个解决方案不提供删除的元素,因为
remove(Object)
只返回一个
boolean
告诉
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"]
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);
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
  } ) );