139

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:

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

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

9
  • 9
    This really seems like a classic case of when to just use a loop and iterator instead. Commented Feb 29, 2016 at 16:28
  • 3
    @chrylis I kindly disagree ;) We're so used to imperative programming, that any other way sounds too exotic. Imagine if reality was the other way round: we are very used to functional programming and a new imperative paradigm is added to Java. Would you say that this would be the classic case for streams, predicates and optionals? Commented Feb 29, 2016 at 17:45
  • 12
    Don't call get() here! You have no idea whether its empty or not. You'll throw an exception if the element was not there. Instead, use one of the safe methods like ifPresent, orElse, orElseGet, or orElseThrow. Commented Feb 29, 2016 at 17:55
  • @FedericoPeraltaSchaffner Probably a matter of taste. But I also would propose not to combine both. When I see some code getting a value using streams I normally assume that the operations in the stream are free of side-effects. Mixing in removing the element might result in code that can be missleading to readers. Commented Feb 29, 2016 at 18:50
  • 1
    Just to clarify: Do you want to remove all the elements in the list for which the Predicate is true or only the first (of a possibly zero, one or many elements)? Commented Feb 29, 2016 at 19:15

14 Answers 14

241

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

Sign up to request clarification or add additional context in comments.

6 Comments

i'd suggest this as the best answer
This is very neat. You might need to implement the equals/hashCode if this is not done for your own objects. (In this example String is used which have them by default).
IMHO the answer does not consider the "get" part: removeIf is an elegant solution to remove elements from a collection, but it does not return the removed element.
FYI removeIf has time complexity O(n). Source: javabrahman.com/java-8/…
This does not answer the question. The requirement is to remove the element from the list and and get removed item/items to a new list.
|
46

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.

4 Comments

Additionally to deleting the element from the list, the OP still wants a reference to the element.
@eee: Thanks a lot for pointing that out. I missed that part from OP's original question.
just to note this will remove all the item which matches to the condition. But OP seems need to remove only the first item (OP used findFirst() )
@asifsid88 how do you get removed element? Isn't the title "get and remove"?
28

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

4 Comments

I like this solution, but note that it has one serious disadvantage you missed: many Iterable implementations have remove() methods that throw UOE. (Not the ones for JDK collections, of course, but I think its unfair to say "works on any Iterable".)
I think we could assume that if an element can be removed in general, it can be removed by iterator
You could assume that, but having looked at hundred of iterator implementations, it would be a bad assumption. (I still like the approach; you're just over-selling it.)
@Brian Goetz: the default implementation of removeIf makes the same assumption, but, of course, it’s defined on Collection rather than Iterable
21

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.

9 Comments

This is pathological for a linked list.
@chrylis The index solution would be indeed. Depending on the list implementation, one would prefer one over the other. Made a small edit.
@chrylis: in case of a LinkedList you perhaps shouldn’t use the stream API as there is no solution without traversing at least twice. But I don’t know of any real life scenario where the academic advantage of a linked list can compensate its actual overhead. So the simple solution is to never use LinkedList.
Oh so many edits… now the first solution doesn’t provide the removed element as remove(Object) only returns a boolean telling whether there was an element to remove or not.
@Marco Stramezzi: unfortunately, the comment explaining it got removed. Without boxed() you get an OptionalInt which can only map from int to int. Unlike IntStream, there is no mapToObj method. With boxed(), you’ll get an Optional<Integer> which allows to map to an arbitrary object, i.e. the ProducerDTO returned by remove(int). The cast from Integer to int is necessary to disambiguate between remove(int) and remove(Object).
|
12

Use can use filter of Java 8, and create another list if you don't want to change the old list:

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

Comments

5

I'm sure this will be an unpopular answer, but it works...

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

p[0] will either hold the found element or be null.

The "trick" here is circumventing the "effectively final" problem by using an array reference that is effectively final, but setting its first element.

6 Comments

It this case, it’s not that bad, but not an improvement over the possibility to just call .orElse(null) to get the ProducerDTO or null
In that case, it might be easier to just have .orElse(null) and have an if, no?
@Holger but how can you invoke remove() too by using orElse(null)?
Just use the result. if(p!=null) producersProcedureActive.remove(p); that’s still shorter than the lambda expression in your ifPresent call.
@holger I interpreted the goal of the question as being avoiding multiple statements - ie a 1-line solution
|
4

With Eclipse Collections you can use detectIndex along with remove(int) on any java.util.List.

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

If you use the MutableList type from Eclipse Collections, you can call the detectIndex method directly on the list.

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

Note: I am a committer for Eclipse Collections

Comments

2

As others have suggested, this might be a use case for loops and iterables. In my opinion, this is the simplest approach. If you want to modify the list in-place, it cannot be considered "real" functional programming anyway. But you could use Collectors.partitioningBy() in order to get a new list with elements which satisfy your condition, and a new list of those which don't. Of course with this approach, if you have multiple elements satisfying the condition, all of those will be in that list and not only the first.

1 Comment

Much better to filter the stream and collect results to a new list
2

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"]

Comments

2

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.

Comments

1

the task is: get ✶and✶ remove element from list

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

Comments

1
resumoRemessaPorInstrucoes.removeIf(item -> 
            item.getTipoOcorrenciaRegistro() == TipoOcorrenciaRegistroRemessa.PEDIDO_PROTESTO.getNome() ||
            item.getTipoOcorrenciaRegistro() == TipoOcorrenciaRegistroRemessa.SUSTAR_PROTESTO_BAIXAR_TITULO.getNome());

2 Comments

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.
1

A variation of the above:

    import static java.util.function.Predicate.not;

    final Optional<MyItem> myItem = originalCollection.stream().filter(myPredicate(someInfo)).findFirst();
    final List<MyItem> myOtherItems = originalCollection.stream().filter(not(myPredicate(someInfo))).toList();

    private Predicate<MyItem> myPredicate(Object someInfo) {
        return myItem -> myItem.someField() == someInfo;
    }

Comments

0

Combining my initial idea and your answers I reached what seems to be the solution to my own question:

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

It lets remove the object using remove(int) that do not traverse again the list (as suggested by @Tunaki) and it lets return the removed object to the function caller.

I read your answers that suggest me to choose safe methods like ifPresent instead of get but I do not find a way to use them in this scenario.

Are there any important drawback in this kind of solution?

Edit following @Holger advice

This should be the function I needed

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

1 Comment

You should not use get and catch the exception. That’s not only bad style but may also cause bad performance. The clean solution is even simpler, return /* stream operation*/.findFirst() .map(i -> producersProcedureActive.remove((int)i)) .orElseGet(() -> { logger.error("No producer found with POD [" + pod + "]"); return null; });

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.