Removing Elements from Java Collections

1. Overview

In this quick tutorial, we’re going to talk about four different ways to remove items from Java Collections that match certain predicates.

We’ll naturally also look at some of the caveats.

2. Defining Our Collection

First, we’re going to illustrate two approaches that mutate the original data structure. Then we’ll talk about two other options that instead of removing the items, will create a copy of the original Collection without them.

Let’s use the following collection throughout our examples to demonstrate how we can achieve the same result using different methods:

Collection<String> names = new ArrayList<>();
names.add("John");
names.add("Ana");
names.add("Mary");
names.add("Anthony");
names.add("Mark");

3. Removing Elements With Iterator

Java’s Iterator enables us to both walk and remove every individual element within a Collection.

To do so, we first need to retrieve an iterator over its elements using the iterator method. Afterward, we can visit each element with the help of next and remove them using remove:

Iterator<String> i = names.iterator();

while(i.hasNext()) {
    String e = i.next();
    if (e.startsWith("A")) {
        i.remove();
    }
}

Despite its simplicity, there are some caveats we should consider:

  • Depending on the collection we may run into ConcurrentModificationException exceptions
  • We need to iterate over the elements before we can remove them
  • Depending on the collection, remove may behave differently than expected. E.g.: ArrayList.Iterator removes the element from the collection and shifts subsequent data to the left whereas, LinkedList.Iterator simply adjusts the pointer to the next element. As such, LinkedList.Iterator performs much better than ArrayList.Iterator when removing items

4. Java 8 and Collection.removeIf()

Java 8 introduced a new method to the Collection interface that provides a more concise way to remove elements using Predicate:

names.removeIf(e -> e.startsWith("A"));

It’s important to note that contrary to the Iterator approach, removeIf performs similarly well in both LinkedList and ArrayList.

In Java 8, ArrayList overrides the default implementation – which relies on Iterator – and implements a different strategy: first, it iterates over the elements and marks the ones that match our Predicate; afterward, it iterates a second time to remove (and shift) the elements that were marked in the first iteration.

5. Java 8 and the Introduction of Stream

One of the new major features in Java 8 was the addition of Stream (and Collectors). There are many ways to create a Stream from a source. However, most operations that affect the Stream instance won’t mutate its source, rather, the API focuses on creating copies of a source and performing any operation we may need in them.

Let’s take a look at how we can use Stream and Collectors to find/filter elements that match, and don’t match, our Predicate.

5.1. Removing Elements With Stream

Removing, or rather, filtering elements using Stream is quite straightforward, we just need to create an instance of Stream using our Collection, invoke filter with our Predicate and then collect the result with the help of Collectors:

Collection<String> filteredCollection = names
  .stream()
  .filter(e -> !e.startsWith("A"))
  .collect(Collectors.toList());

Streaming is less invasive than the previous approaches, it promotes isolation and enables the creation of multiple copies from the same source. However, we should keep in mind that it also increases the memory used by our application.

5.2. Collectors.partitioningBy

Combining both Stream.filter and Collectors is quite handy, although we may run into scenarios where we need both matching and non-matching elements. In such cases we can take advantage of Collectors.partitioningBy:

Map<Boolean, List<String>> classifiedElements = names
    .stream()
    .collect(Collectors.partitioningBy((String e) -> 
      !e.startsWith("A")));

String matching = String.join(",",
  classifiedElements.get(true));
String nonMatching = String.join(",",
  classifiedElements.get(false));

This method returns a Map that only contains two keys, true and false, each pointing to a list that contains the matching, and non-matching elements, respectively.

6. Conclusion

In this article, we looked at some methods to remove elements from Collections and some of their caveats.

You can find the complete source code and all code snippets for this article over on GitHub.

Related posts:

Deque và ArrayDeque trong Java
Find the Registered Spring Security Filters
Join and Split Arrays and Collections in Java
Apache Commons Collections BidiMap
Java Program to Find ith Largest Number from a Given List Using Order-Statistic Algorithm
Java Program to Implement the Checksum Method for Small String Messages and Detect
Java Program to Implement a Binary Search Tree using Linked Lists
Deploy a Spring Boot App to Azure
Java Program to Implement Branch and Bound Method to Perform a Combinatorial Search
Converting between an Array and a List in Java
Send an email with an attachment
Java Program to Check if an UnDirected Graph is a Tree or Not Using DFS
Java Program to Encode a Message Using Playfair Cipher
Java Program to Implement Heap
Converting a Stack Trace to a String in Java
Collect a Java Stream to an Immutable Collection
File Upload with Spring MVC
Java Program to Create the Prufer Code for a Tree
Extract links from an HTML page
Java Program to Perform Partial Key Search in a K-D Tree
Java Program to Test Using DFS Whether a Directed Graph is Weakly Connected or Not
Spring Boot - Admin Server
Handling Errors in Spring WebFlux
Java Deep Learning Essentials - Yusuke Sugomori
Getting Started with Custom Deserialization in Jackson
Java Program to Sort an Array of 10 Elements Using Heap Sort Algorithm
Java Program to Generate Random Hexadecimal Byte
Tạo ứng dụng Java RESTful Client với thư viện Retrofit
Java Program to Find the Peak Element of an Array O(n) time (Naive Method)
Hướng dẫn Java Design Pattern – Object Pool
Sending Emails with Java
Loại bỏ các phần tử trùng trong một ArrayList như thế nào trong Java 8?