In your case I would create the maps first and then loop through the elements in this list as shown:

Map<String, List<MyObject>> rows = new HashMap<>(); 
list.forEach(element -> rows.computeIfAbsent(element.personalId, s -> new ArrayList<>()).add(element));

You can use computeIfAbsent in order to create a new list/map as a value of the map before you can put your data in.


The same is with the second data type you created:

Map<String, Map<Integer, MyObject>> persons = new HashMap<>();
list.forEach(element -> persons.computeIfAbsent(element.personalId, s -> new HashMap<>()).put(element.rowNumber, element));

Here is a way to solve this with streams. But note that the objects must have a unique personId/rowNumber:

Map<String, List<MyObject>> rows = list.stream().collect(
        Collectors.toMap(element -> element.personalId, 
        element -> new ArrayList<MyObject>(Arrays.asList(element))));

As well as for the other map:

    Map<String, Map<Integer, MyObject>> persons = list.stream().collect(
            Collectors.toMap(e -> e.personalId, 
            e -> new HashMap<>(Map.of(e.rowNumber, e))));
Answer from Japhei on Stack Overflow
🌐
How to do in Java
howtodoinjava.com › home › java streams › java stream to map with collectors.tomap() examples
Java Stream to Map With Collectors.toMap() Examples
March 18, 2019 - Because two stream items produced the same key and no merge function was given. Since Java 9, the message names the key and both values, as in Duplicate key 3 (attempted merging values Item3-1 and Item3-2). We either pass a merge function or switch to groupingBy(). No. A null value throws NullPointerException, even when the map type allows null. We replace the null in the value mapper or use collect(HashMap::new, …, Map::putAll), as section 4 shows.
🌐
Medium
medium.com › @AlexanderObregon › javas-collectors-tomap-method-explained-f95999d4ebe6
Java's Collectors.toMap() Method Explained
October 20, 2024 - For example, you might want to convert a list of objects into a map where each object’s property serves as the key, and another property as the value: List<Employee> employees = List.of( new Employee(1, "Kaitlyn"), new Employee(2, "Alex"), ...
🌐
Baeldung
baeldung.com › home › java › java collections › java map › java collectors tomap
Java Collectors toMap | Baeldung
February 26, 2026 - In this article, we looked into the toMap() method of the Collectors class. It allows us to create a new Map from a Stream.
Top answer
1 of 2
9

In your case I would create the maps first and then loop through the elements in this list as shown:

Map<String, List<MyObject>> rows = new HashMap<>(); 
list.forEach(element -> rows.computeIfAbsent(element.personalId, s -> new ArrayList<>()).add(element));

You can use computeIfAbsent in order to create a new list/map as a value of the map before you can put your data in.


The same is with the second data type you created:

Map<String, Map<Integer, MyObject>> persons = new HashMap<>();
list.forEach(element -> persons.computeIfAbsent(element.personalId, s -> new HashMap<>()).put(element.rowNumber, element));

Here is a way to solve this with streams. But note that the objects must have a unique personId/rowNumber:

Map<String, List<MyObject>> rows = list.stream().collect(
        Collectors.toMap(element -> element.personalId, 
        element -> new ArrayList<MyObject>(Arrays.asList(element))));

As well as for the other map:

    Map<String, Map<Integer, MyObject>> persons = list.stream().collect(
            Collectors.toMap(e -> e.personalId, 
            e -> new HashMap<>(Map.of(e.rowNumber, e))));
2 of 2
2

Map<String, Map<Integer, List<MyObject>>> object using streams without using Collectors.groupingBy()

By looking at the map type, I can assume that a combination of personalId and rowNumber is not unique, i.e. there could be multiple occurrences of each combination (otherwise you don't need to group objects into lists). And there could be different rowNumber associated with each personalId. Only if these conclusions correct, this nested collection might have a very vague justification for existence.

Otherwise, you probably can substitute it multiple collections for different use-cases, for Map<String, MyObject> - object by id (if every id is unique):

Map<String, MyObject> objById = myList.stream()
    .collect(Collectors.toMap(
        MyObject::getPersonalId,
        Function.identity()
    ));

I'll proceed assuming that you really need such a nested collection.

Now, let's address the issue with groupingBy(). This collector uses internally Objects.requireNonNull() to make sure that a key produced by the classifier function is non-null.

If you tried to use and failed because of the hostility to null-keys, that implies that either personalId, or rowNumber, or both can be null.

Now let's make a small detour and pose the question of what does it imply if a property that considered to be significant (you're going to use personalId and rowNumber to access the data, hence they are definitely important) and null means first of all?

null signifies the absence of data and nothing else. If in your application null values have an additional special meaning, that's a design flaw. If properties that are significant for managing your data for some reason appear to be null you need to fix that.

You might claim that you're quite comfortable with null values. If so let pause for a moment and imagine a situation: person enters a restaurant, orders a soup and asks the waiter to bring them a fork instead of spoon (because of they have a negative experience with a spoon, and they are enough comfortable with fork).

null isn't a data, it's an indicator of the absence of data, storing null is an antipattern. If you're storing null it obtains a special meaning because you're forced to treat it separately.

To replace personalId and rowNumber that are equal to null with default values we need only one line of code.

public static void replaceNullWithDefault(List<MyObject> list,
                                          String defaultId,
                                          Integer defaultNum) {
    
    list.replaceAll(obj -> obj.getPersonalId() != null && obj.getRowNumber() != null ? obj :
        new MyObject(Objects.requireNonNullElse(obj.getPersonalId(), defaultId),
            Objects.requireNonNullElse(obj.getRowNumber(), defaultNum),
            obj.getDescription()));
}

After that can use the proper tool instead eating soup with a fork, I mean we can process the list data with groupingBy():

public static void main(String[] args) {
    List<MyObject> myList = new ArrayList<>(
        List.of(
            new MyObject("id1", 1, "desc1"),
            new MyObject("id1", 1, "desc2"),
            new MyObject("id1", 2, "desc3"),
            new MyObject("id1", 2, "desc4"),
            new MyObject("id2", 1, "desc5"),
            new MyObject("id2", 1, "desc6"),
            new MyObject("id2", 1, "desc7"),
            new MyObject(null, null, "desc8")
        ));
    
    replaceNullWithDefault(myList, "id0", 0); // replacing null values
    
    Map<String, Map<Integer, List<MyObject>>> byIdAndRow = myList // generating a map
        .stream()
        .collect(Collectors.groupingBy(
            MyObject::getPersonalId,
            Collectors.groupingBy(MyObject::getRowNumber)
        ));
    
    byIdAndRow.forEach((k, v) -> { // printing the map
        System.out.println(k);
        v.forEach((k1, v1) -> System.out.println(k1 + " -> " + v1));
    });
}

Output:

id0
0 -> [MyObject{'id0', 0, 'desc8'}]
id2
1 -> [MyObject{'id2', 1, 'desc5'}, MyObject{'id2', 1, 'desc6'}, MyObject{'id2', 1, 'desc7'}]
id1
1 -> [MyObject{'id1', 1, 'desc1'}, MyObject{'id1', 1, 'desc2'}]
2 -> [MyObject{'id1', 2, 'desc3'}, MyObject{'id1', 2, 'desc4'}]

A link to Online Demo

Now, please pay attention to the usage of groupingBy() did you notice its conciseness. That's the right tool which allows generating even such a clumsy nested map.

And now we're going to eat the soup with a fork! All null properties would be used as is:

public static void main(String[] args) {
    List<MyObject> myList = new ArrayList<>(
        List.of(
            new MyObject("id1", 1, "desc1"),
            new MyObject("id1", 1, "desc2"),
            new MyObject("id1", 2, "desc3"),
            new MyObject("id1", 2, "desc4"),
            new MyObject("id2", 1, "desc5"),
            new MyObject("id2", 1, "desc6"),
            new MyObject("id2", 1, "desc7"),
            new MyObject(null, null, "desc8")
        ));
    
    Map<String, Map<Integer, List<MyObject>>> byIdAndRow = myList // generating a map
        .stream()
        .collect(
            HashMap::new,
            (Map<String, Map<Integer, List<MyObject>>> mapMap, MyObject next) ->
                mapMap.computeIfAbsent(next.getPersonalId(), k -> new HashMap<>())
                    .computeIfAbsent(next.getRowNumber(), k -> new ArrayList<>())
                    .add(next),
            (left, right) -> right.forEach((k, v) -> left.merge(k, v,
                (oldV, newV) -> {
                    newV.forEach((k1, v1) -> oldV.merge(k1, v1,
                        (listOld, listNew) -> {
                            listOld.addAll(listNew);
                            return listOld;
                        }));
                    return oldV;
                }))
        );
    
    byIdAndRow.forEach((k, v) -> { // printing the map
        System.out.println(k);
        v.forEach((k1, v1) -> System.out.println(k1 + " -> " + v1));
    });
}

Output:

null
null -> [MyObject{'null', null, 'desc8'}]
id2
1 -> [MyObject{'id2', 1, 'desc5'}, MyObject{'id2', 1, 'desc6'}, MyObject{'id2', 1, 'desc7'}]
id1
1 -> [MyObject{'id1', 1, 'desc1'}, MyObject{'id1', 1, 'desc2'}]
2 -> [MyObject{'id1', 2, 'desc3'}, MyObject{'id1', 2, 'desc4'}]

A link to Online Demo

🌐
Java Code Geeks
javacodegeeks.com › home › core java
Collectors.toMap() vs Collectors.groupingBy() in Java Streams - Java Code Geeks
June 27, 2024 - ToMapExample.java · public class ToMapExample { public static void main(String[] args) { List<Product> products = List.of( new Product(1, "Shirt", 1200.0, "Clothing"), new Product(2, "Laptop", 2000.0, "Electronics"), new Product(3, "Keyboard", 80.0, "Accesories") ); // Example 1: Basic usage Map<Integer, String> productMap = products.stream().collect(Collectors.toMap( Product::getId, // keyMapper Product::getName // valueMapper )); System.out.println(productMap); } } This block of code demonstrates the basic usage of Collectors.toMap() to convert a list of Product objects into a map.
🌐
GeeksforGeeks
geeksforgeeks.org › java › stream-map-java-examples
Stream map() in Java with examples - GeeksforGeeks
January 4, 2025 - Example 2 : Stream map() function with operation of converting lowercase to uppercase. Java · // Java code for Stream map(Function mapper) // to get a stream by applying the // given function to this stream. import java.util.*; import java.util.stream.Collectors; class GFG { // Driver code public static void main(String[] args) { System.out.println("The stream after applying " + "the function is : "); // Creating a list of Integers List<String> list = Arrays.asList("geeks", "gfg", "g", "e", "e", "k", "s"); // Using Stream map(Function mapper) to // convert the Strings in stream to // UpperCase form List<String> answer = list.stream().map(String::toUpperCase).
🌐
Medium
devblabs.medium.com › java-tomap-stream-collector-a-story-of-ide-weirdness-1d6dd91c435a
Java toMap Stream Collector (a story of IDE weirdness) | by Brian Olson | Medium
October 30, 2020 - At any rate I finally got a working example of modifying nested maps using Java streams. In case you ever need it · import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors;public class WorkingStreams { public static void main(String[] args) { System.out.println("Hello world!"); Map<String, Map<String, String>> outerMap = new HashMap<String, Map<String, String>>(); Map<String, String> innerMap1 = new HashMap<String, String>(); Map<String, String> innerMap2 = new HashMap<String, String>(); innerMap1.put("innerKey1", "innerValue1"); innerMap1.put("innerKey2", "innerVal
🌐
Stack Abuse
stackabuse.com › guide-to-java-8-collectors-tomap
Guide to Java 8 Collectors: toMap()
March 30, 2023 - The return value of the method ... example let's say we'd like to map our student's names to their average grade, that is create a <K, V> pair that has a <name, avgGrade> form....
Find elsewhere
🌐
Medium
medium.com › javarevisited › how-to-use-streams-map-filter-and-collect-methods-in-java-1e13609a318b
How to use map, filter, and collect methods in Java Stream? Example Tutorial | by javinpaul | Javarevisited | Medium
January 25, 2024 - Then, the map() function will do the transformation for you. It is also an intermediate Stream operation which means you can call other Stream methods, like a filter, or collect on this to create a chain of transformations. Now, coming to the filter method, as its name suggests, it filters elements based upon a condition you gave it. For example, if your list contains numbers and you only want numbers, then you can use the filter method to only select a number that is fully divisible by two.
🌐
DigitalOcean
digitalocean.com › community › tutorials › java-stream-collect-method-examples
Java Stream collect() Method Examples | DigitalOcean
Technical tutorials, Q&A, events — This is an inclusive place where developers can find or lend support and discover new ways to contribute to the community.
🌐
Java Guides
javaguides.net › 2020 › 04 › java-8-stream-map-and-collect-example.html
Java 8 Stream - map() and collect() Example
September 14, 2020 - This tutorial explained in below YouTube video: In this example, we create a User and UserDTO class and we use Stream.map() method to convert User into UserDTO class: javapackage com.javaguides.java.tutorial; import java.util.ArrayList; import ...
🌐
GeeksforGeeks
geeksforgeeks.org › java › collectors-tomap-method-in-java-with-examples
Collectors toMap() method in Java with Examples - GeeksforGeeks
December 26, 2025 - Example: This example demonstrates how to use Collectors.toMap() to convert a stream of key–value pairs into a Map when all keys are unique. Java ·
🌐
Medium
medium.com › geekculture › java-streams-collect-a-stream-to-map-40480b96a38
Java Streams: Collect a Stream to Map | Geek Culture
May 7, 2022 - How to Collect a stream back into a list using Collectors . toMap method. Examples with lambda functions, method reference, identity functions an functional programming.
🌐
DZone
dzone.com › coding › languages › java 8 map, filter, and collect examples
Java 8 Map, Filter, and Collect Examples - DZone
June 21, 2018 - This is the same logic we have used while solving coding problems to check if a given number is even or odd in Java. We are almost done. But, so far, we only have the Stream of even integers — not the List of even Integers and that's why we need to use them. Since we need a List, I called collect(Collectors.toList()), which will accumulate all even numbers into a List and return.
🌐
Baeldung
baeldung.com › home › java › java collections › java map › working with maps using streams
Working With Maps Using Streams | Baeldung
November 10, 2025 - Then we’ll present a couple of different problems related to Maps and their concrete solutions using Streams. Learn different techniques for merging maps in Java 8 ... Learn how to use the toMap() method of the Collectors class. ... The article is an example-heavy introduction of the possibilities and operations offered by the Java 8 Stream API.
🌐
Blogger
javarevisited.blogspot.com › 2018 › 05 › java-8-filter-map-collect-stream-example.html
How to use filter + map + collect + Stream in Java? Example Tutorial
May 22, 2018 - Similar to a map, the filter is also an intermediate operation which means you can call other Stream methods after calling filter. The filter() method is also lazy, which means it will not be evaluated until you call a reduction method like collect, and it will stop as soon as it reaches the target. If you are not familiar with Stream behavior, I suggest you check to Learn Java Functional Programming with Lambdas & Streams by Rang Rao Karnam on Udemy, which explains Stream fundamentals in good detail.
🌐
Medium
medium.com › @ajingnv › java-stream-apis-tutorial-how-to-use-collect-and-sorted-with-examples-6ec76d55faef
Java Stream APIs Tutorial: How To Use .collect and .sorted With Example | by Jingnu An | Medium
August 8, 2023 - .collect(Collectors.toMap(Customer::getCustomerId, customer -> customer)): .collect is a terminal operation in the Java 8 Stream API. In this example, the stream elements are collected into a map using the toMap collector.
🌐
Mkyong
mkyong.com › home › java8 › java 8 streams map() examples
Java 8 Streams map() examples | mkyong.com
April 3, 2017 - List<StaffPublic> result = staff.stream().map(temp -> { StaffPublic obj = new StaffPublic(); obj.setName(temp.getName()); obj.setAge(temp.getAge()); if ("mkyong".equals(temp.getName())) { obj.setExtra("this field is for mkyong only!"); } return obj; }).collect(Collectors.toList()); System.out.println(result); } } Output · [ StaffPublic{name='mkyong', age=30, extra='this field is for mkyong only!'}, StaffPublic{name='jack', age=27, extra='null'}, StaffPublic{name='lawrence', age=33, extra='null'} ] Processing Data with Java SE 8 Streams, Part 1 · Java 8 – Filter a Map examples ·
🌐
GeeksforGeeks
geeksforgeeks.org › java › how-to-convert-a-stream-into-a-map-in-java
How to convert a Stream into a Map in Java - GeeksforGeeks
July 15, 2025 - Example 1: Here, we will convert a string into a Map with the keys as the words of the string and the value as the length of each word. ... // Program to convert // the Stream to Map import java.io.*; import java.util.stream.*; import ...
🌐
Medium
medium.com › @AlexanderObregon › javas-collectors-mapping-method-explained-661ce6ce5bd2
Java’s Collectors.mapping() Method Explained | Medium
November 27, 2024 - Be cautious when working with large or complex streams. Suppose we have a list of employees, and we need to group them by department while collecting only their names. The mapping() method allows us to apply a function to extract the names and ...