CodeGym /Courses /JAVA 25 SELF /Collection transformation

Collection transformation

JAVA 25 SELF
Level 28 , Lesson 1
Available

1. Transforming collection elements

In programming, one of the most common operations is a collection transformation: we have a collection of data of one type and, based on it, need to create a new collection of another type. For example, from a list of User objects obtain a list of their names (String), or from a list of numbers — a list of their squares.

The most fundamental and straightforward way in Java is to use the imperative approach, i.e., a regular for loop (often — for-each).

Example: from a list of strings get a list of their lengths

Suppose we have a list of city names:

List<String> cities = List.of("London", "Paris", "Tokyo", "New York");

Our task is to create a new list that contains the length of each name. The final result should be: [6, 5, 5, 8].

A solution using a for loop:

import java.util.ArrayList;
import java.util.List;

public class CollectionTransform {
    public static void main(String[] args) {
        List<String> cities = List.of("London", "Paris", "Tokyo", "New York");
        List<Integer> lengths = new ArrayList<>(); // Create a new, empty list for the result

        for (String city : cities) {
            // For each element from cities we compute its length...
            int length = city.length();
            // ...and add this result to the new list
            lengths.add(length);
        }

        System.out.println(lengths); // Prints: [6, 5, 5, 8]
    }
}

We start by creating a new empty collection for results — the transformation does not modify the source collection. To traverse the source we use a for-each loop and inside apply the required logic (length()) to each element, then add the result to the new list via add.

2. Transforming objects

We often work with more complex data types. Suppose we have a Product class, and we need to obtain a list of its names (or prices).

public class Product {
    private String name;
    private double price;

    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }
}

Now, given a list of products, let’s obtain a list of their names:

import java.util.ArrayList;
import java.util.List;

public class ProductExample {
    public static void main(String[] args) {
        List<Product> products = List.of(
            new Product("Laptop", 1200.0),
            new Product("Mouse", 25.5),
            new Product("Keyboard", 75.0)
        );

        // Create a new list for names
        List<String> productNames = new ArrayList<>();

        for (Product product : products) {
            // For each Product object, get its name
            productNames.add(product.getName());
        }

        System.out.println(productNames); // Prints: [Laptop, Mouse, Keyboard]
    }
}

The logic is the same: iterate over the source collection, apply the needed method (for example, getName()) to each element, and add the result to the new collection.

3. Working with nested collections

Consider a case with a list of lists: several departments, each with its own list of employees. The task is to obtain one overall (“flat”) list of all employees.

Example: merging lists of employees

List<List<String>> departments = List.of(
    List.of("Anna", "Boris"),
    List.of("Victoria", "Gleb", "Dmitry"),
    List.of("Elena")
);

We want to obtain a single list: [Anna, Boris, Victoria, Gleb, Dmitry, Elena].

Approach 1: using addAll()

import java.util.ArrayList;
import java.util.List;

public class NestedCollectionExample {
    public static void main(String[] args) {
        List<List<String>> departments = List.of(
            List.of("Anna", "Boris"),
            List.of("Victoria", "Gleb", "Dmitry"),
            List.of("Elena")
        );

        List<String> allEmployees = new ArrayList<>();

        // Iterate over each list (department)
        for (List<String> department : departments) {
            // Add all elements from the current list to the combined list
            allEmployees.addAll(department);
        }

        System.out.println(allEmployees); // [Anna, Boris, Victoria, Gleb, Dmitry, Elena]
    }
}

Approach 2: a nested loop — the same thing, but “manually”:

List<List<String>> departments = List.of(...);
List<String> allEmployees = new ArrayList<>();

for (List<String> department : departments) { // Outer loop over departments
    for (String employee : department) {      // Inner loop over employees
        allEmployees.add(employee);
    }
}

System.out.println(allEmployees);

Both approaches are equivalent in result. The addAll() method essentially encapsulates the logic of the nested loop and makes the code shorter.

4. Advanced cases and condition-based transformations

Sometimes the transformation should be performed only for elements that satisfy a condition. For example, get a list of city names that start with the letter "N" and take their lengths. Here we combine filtering and transformation: if + a method call (length()).

import java.util.ArrayList;
import java.util.List;

public class ConditionalTransform {
    public static void main(String[] args) {
        List<String> cities = List.of("London", "Paris", "Tokyo", "New York", "Nuremberg");
        List<Integer> lengths = new ArrayList<>();

        for (String city : cities) {
            // First, check the condition
            if (city.startsWith("N")) {
                // If the condition is met, apply the transformation
                lengths.add(city.length());
            }
        }

        System.out.println(lengths); // Prints: [8, 9]
    }
}

The pattern is this: inside the loop, first filter an element via a condition (startsWith, comparison, range check, etc.), then apply the required transformation and put the result into a new list.

5. Common mistakes and pitfalls

Mistake #1: modifying the source collection during iteration. A frequent mistake is trying to add/remove elements from the source collection directly in a for-each loop. This leads to errors and unpredictable behaviour. The solution: always create a new list for results and populate it.

Mistake #2: incorrect casting. If you work with “raw” collections (for example, via Object) and cast elements to the wrong type, you will get a ClassCastException. Use generics (List<T>) and pay attention to signatures.

Mistake #3: inefficient use of resources. For very large collections, constantly creating new lists and copying elements can be noticeable in both memory and time. In most everyday tasks this is acceptable, but when processing large volumes of data, consider complexity and, if necessary, optimise the approach.

1
Task
JAVA 25 SELF, level 28, lesson 1
Locked
Forming the list of students for the graduation album 🎓
Forming the list of students for the graduation album 🎓
1
Task
JAVA 25 SELF, level 28, lesson 1
Locked
Combining Treasures from Different Chests 💰
Combining Treasures from Different Chests 💰
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION