Java 8 lambda get and remove element from list

后端 未结 12 1554
说谎
说谎 2020-12-08 12:47

Given a list of elements, I want to get the element with a given property and remove it from the list. The best solution I found is:

Produce         


        
相关标签:
12条回答
  • 2020-12-08 13:12

    The below logic is the solution without modifying the original list

    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"]
    
    0 讨论(0)
  • 2020-12-08 13:17

    Although the thread is quite old, still thought to provide solution - using Java8.

    Make the use of removeIf function. Time complexity is O(n)

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

    API reference: removeIf docs

    Assumption: producersProcedureActive is a List

    NOTE: With this approach you won't be able to get the hold of the deleted item.

    0 讨论(0)
  • 2020-12-08 13:17

    The direct solution would be to invoke ifPresent(consumer) on the Optional returned by findFirst(). This consumer will be invoked when the optional is not empty. The benefit also is that it won't throw an exception if the find operation returned an empty optional, like your current code would do; instead, nothing will happen.

    If you want to return the removed value, you can map the Optional to the result of calling remove:

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

    But note that the remove(Object) operation will again traverse the list to find the element to remove. If you have a list with random access, like an ArrayList, it would be better to make a Stream over the indexes of the list and find the first index matching the predicate:

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

    With this solution, the remove(int) operation operates directly on the index.

    0 讨论(0)
  • 2020-12-08 13:19

    When we want to get multiple elements from a List into a new list (filter using a predicate) and remove them from the existing list, I could not find a proper answer anywhere.

    Here is how we can do it using Java Streaming API partitioning.

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

    This way you effectively remove the filtered elements from the original list and add them to a new list.

    Refer the 5.2. Collectors.partitioningBy section of this article.

    0 讨论(0)
  • 2020-12-08 13:21

    To Remove element from the list

    objectA.removeIf(x -> conditions);
    

    eg:

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

    OUTPUT: A B C

    0 讨论(0)
  • 2020-12-08 13:23

    Consider using vanilla java iterators to perform the task:

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

    Advantages:

    1. It is plain and obvious.
    2. It traverses only once and only up to the matching element.
    3. You can do it on any Iterable even without stream() support (at least those implementing remove() on their iterator).

    Disadvantages:

    1. You cannot do it in place as a single expression (auxiliary method or variable required)

    As for the

    Is it possible to combine get and remove in a lambda expression?

    other answers clearly show that it is possible, but you should be aware of

    1. Search and removal may traverse the list twice
    2. ConcurrentModificationException may be thrown when removing element from the list being iterated
    0 讨论(0)
提交回复
热议问题