foreach-ui logo
codeLanguages
account_treeDSA

Quick Actions

quizlock Random Quiz
trending_uplock Progress
  • 1
  • 2
  • 3
  • 4
  • 5
  • quiz
Java
  • Use forEach, removeIf, and replaceAll methods
  • Master Map operations like compute and merge
  • Understand the basics of Stream API
  • Apply lambdas for collection transformations

Lambdas with Collections

You know lambda syntax. You know method references. Time to put them to work with collections—where they really shine.

Sorting with Lambdas

forEach() - Iterate Over Elements

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// Traditional loop
for (String name : names) {
    System.out.println(name);
}

// With lambda
names.forEach(name -> System.out.println(name));

// With method reference
names.forEach(System.out::println);

Works with all Iterable types:

Set<Integer> numbers = Set.of(1, 2, 3, 4, 5);
numbers.forEach(n -> System.out.println(n * 2));

Map<String, Integer> ages = Map.of("Alice", 30, "Bob", 25);
ages.forEach((name, age) -> System.out.println(name + ": " + age));

removeIf() - Conditional Removal

Remove elements matching a predicate:

List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "", "Charlie", ""));

// Remove empty strings
names.removeIf(name -> name.isEmpty());
// Or: names.removeIf(String::isEmpty);

System.out.println(names);  // [Alice, Bob, Charlie]

More examples:

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));

// Remove even numbers
numbers.removeIf(n -> n % 2 == 0);
// Result: [1, 3, 5]

// Remove values less than 3
numbers.removeIf(n -> n < 3);

replaceAll() - Transform All Elements

Replace each element using a function:

List<String> names = new ArrayList<>(Arrays.asList("alice", "bob", "charlie"));

// Convert to uppercase
names.replaceAll(name -> name.toUpperCase());
// Or: names.replaceAll(String::toUpperCase);

System.out.println(names);  // [ALICE, BOB, CHARLIE]
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));

// Double each number
numbers.replaceAll(n -> n * 2);
// Result: [2, 4, 6, 8, 10]

sort() with Comparator

List<String> names = new ArrayList<>(Arrays.asList("Charlie", "Alice", "Bob"));

// Sort alphabetically
names.sort((a, b) -> a.compareTo(b));
// Or: names.sort(String::compareTo);

// Sort by length
names.sort((a, b) -> a.length() - b.length());
// Or: names.sort(Comparator.comparingInt(String::length));

// Sort in reverse
names.sort((a, b) -> b.compareTo(a));
// Or: names.sort(Comparator.reverseOrder());

Using Comparator factory methods:

List<Person> people = new ArrayList<>();

// Sort by name
people.sort(Comparator.comparing(Person::getName));

// Sort by age descending
people.sort(Comparator.comparing(Person::getAge).reversed());

// Multi-level sort
people.sort(Comparator
    .comparing(Person::getLastName)
    .thenComparing(Person::getFirstName)
    .thenComparing(Person::getAge));

Map Operations with Lambdas

compute() - Compute a Value

Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 10);

// Add 5 to Alice's score (or set to 5 if not exists)
scores.compute("Alice", (key, value) -> value == null ? 5 : value + 5);
// Alice -> 15

scores.compute("Bob", (key, value) -> value == null ? 5 : value + 5);
// Bob -> 5 (new entry)

computeIfAbsent() - Lazy Initialization

Only compute if key is absent:

Map<String, List<String>> groups = new HashMap<>();

// Traditional approach
if (!groups.containsKey("team1")) {
    groups.put("team1", new ArrayList<>());
}
groups.get("team1").add("Alice");

// With computeIfAbsent
groups.computeIfAbsent("team1", key -> new ArrayList<>()).add("Alice");
groups.computeIfAbsent("team1", key -> new ArrayList<>()).add("Bob");
// team1 -> [Alice, Bob]

computeIfPresent() - Update Existing

Only compute if key exists:

Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 10);

scores.computeIfPresent("Alice", (key, value) -> value + 5);  // 15
scores.computeIfPresent("Bob", (key, value) -> value + 5);    // No effect, Bob doesn't exist

merge() - Combine Values

Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 10);

// Add 5 to existing value, or set to 5 if absent
scores.merge("Alice", 5, (oldVal, newVal) -> oldVal + newVal);  // 15
scores.merge("Bob", 5, (oldVal, newVal) -> oldVal + newVal);    // 5

// Simplified with Integer::sum
scores.merge("Alice", 5, Integer::sum);

getOrDefault()

Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 10);

int aliceScore = scores.getOrDefault("Alice", 0);  // 10
int bobScore = scores.getOrDefault("Bob", 0);      // 0 (default)

Introduction to Streams

Streams provide a functional approach to processing collections:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

// Traditional approach
List<String> result = new ArrayList<>();
for (String name : names) {
    if (name.length() > 4) {
        result.add(name.toUpperCase());
    }
}

// Stream approach
List<String> result = names.stream()
    .filter(name -> name.length() > 4)
    .map(String::toUpperCase)
    .collect(Collectors.toList());
// [ALICE, CHARLIE, DAVID]

Common Stream Operations

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// filter - keep elements matching predicate
numbers.stream()
    .filter(n -> n % 2 == 0)  // Keep even numbers
    .forEach(System.out::println);  // 2, 4, 6, 8, 10

// map - transform elements
numbers.stream()
    .map(n -> n * n)  // Square each number
    .forEach(System.out::println);  // 1, 4, 9, 16, ...

// reduce - combine into single value
int sum = numbers.stream()
    .reduce(0, (a, b) -> a + b);  // 55
// Or: .reduce(0, Integer::sum);

// count
long count = numbers.stream()
    .filter(n -> n > 5)
    .count();  // 5

// findFirst / findAny
Optional<Integer> first = numbers.stream()
    .filter(n -> n > 5)
    .findFirst();  // Optional[6]

// anyMatch / allMatch / noneMatch
boolean hasEven = numbers.stream().anyMatch(n -> n % 2 == 0);  // true
boolean allPositive = numbers.stream().allMatch(n -> n > 0);   // true
boolean noneNegative = numbers.stream().noneMatch(n -> n < 0); // true

Collecting Results

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// To List
List<String> upperList = names.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

// To Set
Set<String> upperSet = names.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toSet());

// To Map
Map<String, Integer> nameLengths = names.stream()
    .collect(Collectors.toMap(
        name -> name,           // key
        name -> name.length()   // value
    ));
// {Alice=5, Bob=3, Charlie=7}

// Joining strings
String joined = names.stream()
    .collect(Collectors.joining(", "));
// "Alice, Bob, Charlie"

Best Practices

1. Prefer Method References When Possible

// Good
names.forEach(System.out::println);
names.sort(String::compareToIgnoreCase);

// Less clear
names.forEach(n -> System.out.println(n));

2. Keep Lambdas Simple

// Complex lambda - extract to method
list.stream()
    .filter(this::isValidItem)  // Extracted method
    .map(this::transformItem)
    .collect(Collectors.toList());

3. Use Appropriate Collection Methods

// Use removeIf instead of iterator
names.removeIf(String::isEmpty);  // Good
// vs manual iterator removal

// Use replaceAll for in-place transformation
names.replaceAll(String::toUpperCase);  // Good
// vs creating new list

Lambdas make collection operations concise. forEach iterates, removeIf removes conditionally, replaceAll transforms in-place, sort with comparators, compute calculates map values, merge combines values. Streams provide functional pipelines. Method references improve readability. Modern Java collections embrace functional style.

© 2026 forEach. All rights reserved.

Privacy Policy•Terms of Service