1. Introduction
In programming, we constantly encounter situations where you need to select only the required elements from a large data set — that is filtering. Want to keep only even numbers, find strings containing the word "Java", or select users older than 18 — all of these are filtering tasks.
In Java, such operations appear at every step, so it is important to be confident with the different ways to perform them and to understand their specifics.
2. Filtering with a loop
Let’s start with the most basic approach — a plain for loop. This approach is called imperative because you explicitly specify what to do and how. It reads well and is easy to understand.
Example: keep only even numbers
import java.util.*;
public class FilterExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> evenNumbers = new ArrayList<>(); // Create a new list for the result
for (Integer n : numbers) {
if (n % 2 == 0) { // Check the condition: even number
evenNumbers.add(n);
}
}
System.out.println("Even numbers: " + evenNumbers);
}
}
Output:
Even numbers: [2, 4, 6, 8, 10]
It’s important to remember: the original collection (numbers) does not change. We build a new result list.
Example: filter strings by substring
List<String> words = Arrays.asList("java", "python", "javascript", "kotlin", "c++");
List<String> javaWords = new ArrayList<>();
for (String word : words) {
if (word.contains("java")) {
javaWords.add(word);
}
}
System.out.println(javaWords); // [java, javascript]
Same principle: iterate over the list of strings and check for the substring using the contains method.
3. Removing elements from a collection: why isn’t it that simple?
What’s the catch?
Sometimes you want to remove all unnecessary elements from the original collection. But if you try to do this while iterating with a for-each loop, you will get a ConcurrentModificationException.
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));
for (Integer n : numbers) {
if (n < 0) {
numbers.remove(n); // DANGEROUS! ConcurrentModificationException!
}
}
Output:
Exception in thread "main" java.util.ConcurrentModificationException
Collections don’t like being modified during this kind of traversal — the iteration breaks.
How to remove elements from a collection correctly?
Method 1: use Iterator.remove()
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
Integer n = it.next();
if (n < 0) {
it.remove(); // Remove safely!
}
}
System.out.println(numbers); // [1, 3, 5]
Here we create an iterator with iterator(), walk the collection using hasNext() and next(), and remove unwanted elements by calling it.remove().
Method 2: build a new list with only the needed elements
List<Integer> numbers = Arrays.asList(1, -2, 3, -4, 5);
List<Integer> positive = new ArrayList<>();
for (Integer n : numbers) {
if (n >= 0) {
positive.add(n);
}
}
System.out.println(positive); // [1, 3, 5]
We don’t touch the original collection and collect a new list. The approach is safe, readable, and easy to extend as conditions get more complex.
Method 3: use removeIf (Java 8+)
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));
numbers.removeIf(n -> n < 0);
System.out.println(numbers); // [1, 3, 5]
In a single line you pass a condition: “delete everything less than zero.” The collection takes care of the internal details of safe modification.
Bottom line: do not use for-each for removing elements. Choose between Iterator.remove(), creating a new list, or the concise removeIf.
4. When to use which approach for filtering?
The imperative approach using a plain loop is convenient when you need not only to select elements but also to immediately do something with them (for example, print or transform). It is simple and transparent.
If it’s specifically about removing from the original collection, avoid removing in for-each. Use Iterator.remove() for step-by-step control or the modern removeIf() — the shortest and most expressive option.
Example: there’s a list of words; delete all words shorter than four characters:
List<String> words = new ArrayList<>(Arrays.asList("Java", "is", "fun", "awesome", "code"));
words.removeIf(word -> word.length() < 4);
System.out.println(words); // [Java, awesome, code]
The removeIf method takes a predicate — a filtering rule — and removes everything that matches it.
5. Common mistakes when filtering collections
Mistake #1: trying to remove elements from a collection inside a for-each loop.
Such code will lead to a ConcurrentModificationException:
for (Integer n : numbers) {
if (n < 0) {
numbers.remove(n); // BOOM! ConcurrentModificationException
}
}
Correct options: use Iterator.remove() or removeIf.
Mistake #2: an incorrectly formulated filter condition.
You need to remove only negative numbers:
List<Integer> numbers = new ArrayList<>(Arrays.asList(-3, -1, 0, 2, 4));
numbers.removeIf(n -> n < 0);
System.out.println(numbers); // [0, 2, 4]
But if by mistake you write “non‑positive” and use <=, zero will also disappear:
numbers.removeIf(n -> n <= 0); // Result: [2, 4] — zero removed by mistake
Be precise with conditions: a single incorrect comparison completely changes the result.
GO TO FULL VERSION