Contents
- forEach() — iterate with side effects
- removeIf() — conditional removal
- replaceAll() — transform each element
- sort() — sort with Comparator lambda
- Chaining operations
Iterable.forEach(Consumer<T>) replaces the classic enhanced for-loop when you want to perform a side-effect action on each element.
List<String> names = List.of("Alice", "Bob", "Charlie");
// Classic for-loop
for (String name : names) {
System.out.println(name);
}
// Lambda equivalent
names.forEach(name -> System.out.println(name));
// Method reference (most concise)
names.forEach(System.out::println);
forEach() is a terminal operation that cannot break early (unlike a for-loop). If you need to stop early, use a regular loop or Stream.takeWhile() (Java 9+).
Collection.removeIf(Predicate<T>) removes all elements that match the given predicate.
It uses an Iterator internally, so it is safe from ConcurrentModificationException.
List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6, 7, 8));
// Remove all even numbers
numbers.removeIf(n -> n % 2 == 0);
System.out.println(numbers); // [1, 3, 5, 7]
// Remove strings shorter than 4 characters
List<String> words = new ArrayList<>(List.of("hi", "hello", "hey", "world"));
words.removeIf(w -> w.length() < 4);
System.out.println(words); // [hello, world]
Compare with the old iterator-based approach:
// Old way (verbose)
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
if (it.next() % 2 == 0) {
it.remove();
}
}
List.replaceAll(UnaryOperator<T>) replaces each element with the result of applying the operator,
modifying the list in-place.
List<String> fruits = new ArrayList<>(List.of("apple", "banana", "cherry"));
// Uppercase every element
fruits.replaceAll(String::toUpperCase);
System.out.println(fruits); // [APPLE, BANANA, CHERRY]
// Trim and append a suffix
List<String> tags = new ArrayList<>(List.of(" java ", " spring ", " kafka "));
tags.replaceAll(t -> t.trim() + "-tag");
System.out.println(tags); // [java-tag, spring-tag, kafka-tag]
// Double every integer
List<Integer> nums = new ArrayList<>(List.of(1, 2, 3, 4));
nums.replaceAll(n -> n * 2);
System.out.println(nums); // [2, 4, 6, 8]
replaceAll() modifies the list in-place — it does not work on immutable lists created with List.of() (throws UnsupportedOperationException). Wrap with new ArrayList<>(...) first.
List.sort(Comparator<T>) sorts the list in-place. Pass null for natural ordering.
Comparator.comparing() provides a fluent way to build comparators.
List<String> names = new ArrayList<>(List.of("Charlie", "alice", "Bob"));
// Natural (case-sensitive) order
names.sort(null); // or names.sort(Comparator.naturalOrder())
System.out.println(names); // [Bob, Charlie, alice] (uppercase before lowercase)
// Case-insensitive sort
names.sort(String.CASE_INSENSITIVE_ORDER);
System.out.println(names); // [alice, Bob, Charlie]
// Sort by length, then alphabetically
names.sort(Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder()));
// Descending order
names.sort(Comparator.reverseOrder());
// Sort custom objects by a field
List<Employee> employees = getEmployees();
employees.sort(Comparator.comparing(Employee::getName));
employees.sort(Comparator.comparingInt(Employee::getAge).reversed());
For a read-only pipeline (filtering + transforming + collecting), use the Streams API which is better suited than mutating the list in-place:
List<String> words = List.of("hello", "world", "java", "lambda", "hi");
// Keep words longer than 3 chars, uppercase them, sort, collect
List<String> result = words.stream()
.filter(w -> w.length() > 3)
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
System.out.println(result); // [HELLO, JAVA, LAMBDA, WORLD]
When you need to mutate the original list use removeIf() + replaceAll() + sort().
When you need a new transformed collection, prefer Streams.