I think flatMap() is what you're looking for.

For example:

 List<AClass> allTheObjects = map.values()
         .stream()
         .flatMap(listContainer -> listContainer.lst.stream())
         .collect(Collectors.toList());
Answer from Puce on Stack Overflow
🌐
Coderwall
coderwall.com › p › im4lja › joining-objects-into-a-string-with-java-8-stream-api
Joining Objects into a String with Java 8 Stream API (Example)
July 17, 2023 - #join · #stream · You can leverage Java 8 Collectors to concatenate some objects into a string separated by a delimiter: For example: List<Integer> numbers = Arrays.asList( 4, 8, 15, 16, 23, 42 ); return numbers.stream() .map( n -> n.toString() ) .collect( Collectors.joining( "," ) ); will return "4,8,15,16,23,42".
🌐
Davidvlijmincx
davidvlijmincx.com › home › java › java stream join lists
Java Stream join lists
March 10, 2022 - The easiest way to join two lists with Streams is by using the Stream.concat(Stream a, Stream b) method. The method takes two Stream instances as parameters and puts the second Stream behind the first Stream.
🌐
Baeldung
baeldung.com › home › java › java collections › java – combine multiple collections
Java - Combine Multiple Collections | Baeldung
May 11, 2024 - The Stream interface in Java API provides useful methods that make it easier to process collections.
🌐
GeeksforGeeks
geeksforgeeks.org › java › java-8-streams-collectors-joining-method-with-examples
Java 8 Streams | Collectors.joining() method with Examples - GeeksforGeeks
January 22, 2026 - Collectors.joining() concatenates all the string elements into one single String. The final concatenated string is stored in chString. ... import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class GFG { ...
🌐
Medium
buddhiprabhath.medium.com › java-8-streams-flatmap-to-join-list-of-lists-30245a5e31a1
Java 8 streams - flatMap to join list of lists | by Buddhi Prabhath | Medium
September 18, 2020 - We can use flatMap method to join list of lists, as flatMap helps to join streams. let’s say we have a List<List<T>>, and we want to join all the List<T> inside the parent list together as a single list as a List<T>. ... import java.util....
🌐
Vultr Docs
docs.vultr.com › java › examples › join two lists
Java Program to Join Two Lists
December 16, 2024 - Ensure you have two lists to combine. Use Stream.concat() from Java 8 to merge the streams of both lists, then collect results into a new list.
🌐
Techie Delight
techiedelight.com › home › java › join two lists in java
Join two lists in Java | Techie Delight
July 7, 2026 - This method is similar to List.addAll() but likely to run significantly faster. We can also use Java 8 Stream to join two lists. ⮚ Using Stream.of() with flatMap() + Collector · Here we’re obtaining a stream consisting of elements from both the list using the static factory method Stream.of() and accumulating all elements into a new list using a Collector.
Find elsewhere
🌐
Benchresources
benchresources.net › home › java › java 8 – how to merge/concatenate/join two lists into single list ?
Java 8 - How to merge/concatenate/join two lists into single list ? - BenchResources.Net
July 19, 2022 - Previously we have seen an example to join or concatenate two or more Arrays into single Arrays ... package in.bench.resources.java8.merge.list; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class MergeListUsingStreamConcat { public static void main(String[] args) { // list 1 - fruits List<String> fruits = Arrays.asList( "Apple", "Mango", "Grape", "Cherry", "Melon" ); System.out.println("Fruit List :- \n" + fruits); // list 2 - vegetables List<String> veggies = Arrays.asList( "Carrot", "Radish", "Lettuce", "Bean", "Gourd" ); System.out.println("\nVeggies List :- \n" + veggies); // Merging 2 lists List<String> mergedList = Stream .concat(fruits.stream(), veggies.stream()) .collect(Collectors.toList()); // print merged list to the console System.out.println("\nMerged List :- \n" + mergedList); } }
🌐
Medium
medium.com › @AlexanderObregon › javas-collectors-joining-method-explained-a4adb120b785
Java’s Collectors.joining() Method Explained | Medium
November 2, 2024 - Another common use case for Collectors.joining() is building file paths from a list of directory names or file segments. Instead of manually stitching together strings with slashes or other separators, you can use this method to create the path ...
🌐
Baeldung
baeldung.com › home › java › java streams › merging streams in java
Merging Streams in Java | Baeldung
June 24, 2026 - Since it is an instance method, we can easily chain it and append multiple streams. Note that we could also create a List out of the stream by using toList() if we type the resultingStream variable to the StreamEx type.
🌐
Medium
medium.com › @ravindra.cse.tu › java-8-streams-joining-strings-with-delimiters-e641c6b42280
Java 8 Streams: Joining Strings with Delimiters | by Ravindra K Yadav | Medium
November 24, 2024 - package com.program.java8; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class Solution10 { public static void main(String[] args) { List<String> listString = Arrays.asList("Facebook","Apple","Microsoft"); String joinedString = listString.stream().collect(Collectors.joining(",","[","]")); System.out.println(joinedString); } }
🌐
Java Development Journal
javadevjournal.com › home › how to join multiple lists in java
How to Join Multiple Lists in Java | Java Development Journal
October 23, 2020 - In this short post, we will learn how to join multiple lists in Java. We will be covering how to do this in Java 8, Java 7 and how we can use Apache Common API to join these lists. Java 8 Stream API provides a couple of different options to join multiple lists.
🌐
Educative
educative.io › answers › how-to-merge-two-lists-in-java
How to merge two lists in Java
In this shot, we have seen how to merge two lists in Java by using the addAll() method and Java Stream.
🌐
HowToDoInJava
howtodoinjava.com › home › java 8 › join or append stream elements in java
Join or Append Stream Elements in Java
December 13, 2024 - It works well when converting a collection or array of strings to a single string. The String.join() requires a collection or array as input, so we must convert the stream into a list using toList().
🌐
Medium
medium.com › javarevisited › nuanced-string-joining-in-java-7db547c331d0
Nuanced String Joining in Java | Javarevisited
April 23, 2024 - Here we use the ArrayAdapter, convert it to a LazyIterable (which is an Iterable) and collect String instances using String::valueOf. The most surprising thing I didn’t know about String.join is that there are no public overloads that take a prefix and suffix, like Collectors.joining has. We can also use Stream to turn the Integer array into an Iterable<String>.
🌐
Scaler
scaler.com › home › topics › java program to merge two lists
Java Program to Merge Two Lists | Scaler Topics
June 9, 2024 - stream class in java can be used for merging two lists, it accepts two streams as arguments and joins them together to form a new stream.
🌐
Crunchify
crunchify.com › java j2ee tutorials › in java how to join list of objects? collectors.joining concatenates the input elements, delimiter
In Java How to join List of Objects? Collectors.joining Concatenates the input Elements, Delimiter • Crunchify
August 30, 2022 - Create test class CrunchifyCompanyListJoinerTutorial.java · We will create a list with two objects of class CrunchifyCompany · Iterate through list elements and get all company Name and Address using · crunchifyList.stream().map(crunchify -> crunchify.getCompanyName()) Use collect(Collectors.joining(” : “, “<< “, ” >>”)) to join results ·
🌐
How to do in Java
howtodoinjava.com › home › collections framework › java arraylist › how to merge two arraylists in java
How to Merge Two ArrayLists in Java
January 19, 2023 - ArrayList<String> listOne = new ArrayList<>(Arrays.asList("a", "b", "c")); ArrayList<String> listTwo = new ArrayList<>(Arrays.asList("c", "d", "e")); List<String> combinedList = Stream.of(listOne, listTwo) .flatMap(x -> x.stream()) .collect(Collectors.toList()); System.out.println(combinedList); Program output. ... In these examples, we combined the lists, but in the final list, we had duplicate elements. This may not be the desired output in many cases. Next, we will learn to merge the lists, excluding duplicates. To get a merged list minus duplicate elements, we have two approaches: The Java Sets allow only unique elements.
Top answer
1 of 2
1

The general idea has been sketched in the comments: iterate one list, create a map whose keys are the attributes you want to join by, then iterate the other list and check if there's an entry in the map. If there is, get the value from the map and create a new object from the value of the map and the actual element of the list.

It's better to create a map from the list with the higher number of joined elements. Why? Because searching a map is O(1), no matter the size of the map. So, if you create a map from the list with the higher number of joined elements, then, when you iterate the second list (which is smaller), you'll be iterating among less elements.

Putting all this in code:

public static <B, S, J, R> List<R> join(
    List<B> bigger, 
    List<S> smaller,
    Function<B, J> biggerKeyExtractor,
    Function<S, J> smallerKeyExtractor,
    BiFunction<B, S, R> joiner) {

    Map<J, List<B>> map = new LinkedHashMap<>();
    bigger.forEach(b -> 
        map.computeIfAbsent(
                biggerKeyExtractor.apply(b),
                k -> new ArrayList<>())
            .add(b));

    List<R> result = new ArrayList<>();
    smaller.forEach(s -> {
        J key = smallerKeyExtractor.apply(s);
        List<B> bs = map.get(key);
        if (bs != null) {
            bs.forEach(b -> {
                R r = joiner.apply(b, s);
                result.add(r);
            }
        }
    });

    return result;
}

This is a generic method that joins bigger List<B> and smaller List<S> by J join keys (in your case, as the join key is a composite of String and Integer types, J will be List<Object>). It takes care of duplicates and returns a result List<R>. The method receives both lists, functions that will extract the join keys from each list and a joiner function that will create new result R elements from joined B and S elements.

Note that the map is actually a multimap. This is because there might be duplicates as per the biggerKeyExtractor join function. We use Map.computeIfAbsent to create this multimap.

You should create a class like this to store joined results:

public class JoinedResult {

    private final NameProperties properties;
    private final NameEntries entries;

    public JoinedResult(NameProperties properties, NameEntries entries) {
        this.properties = properties;
        this.entries = entries;
    }

    // TODO getters
}

Or, if you are in Java 14+, you might just use a record:

public record JoinedResult(NameProperties properties, NameEntries entries) { }

Or actually, any Pair class from out there will do, or you could even use Map.Entry.

With the result class (or record) in place, you should call the join method this way:

long propertiesSize = namePropertiesList.stream()
    .map(p -> Arrays.asList(p.getMonths(), p.getId()))
    .distinct()
    .count();
long entriesSize = nameEntriesList.steram()
    .map(e -> Arrays.asList(e.getMonths(), e.getId()))
    .distinct()
    .count();

List<JoinedResult> result = propertiesSize > entriesSize ? 
    join(namePropertiesList, 
         nameEntriesList, 
         p -> Arrays.asList(p.getMonths(), p.getId()),
         e -> Arrays.asList(e.getMonths(), e.getId()),
         JoinedResult::new)                                    :
    join(nameEntriesList, 
         namePropertiesList, 
         e -> Arrays.asList(e.getMonths(), e.getId()),
         p -> Arrays.asList(p.getMonths(), p.getId()),
         (e, p) -> new JoinedResult(p, e));

The key is to use generics and call the join method with the right arguments (they are flipped, as per the join keys size comparison).

Note 1: we can use List<Object> as the key of the map, because all Java lists implement equals and hashCode consistently (thus they can safely be used as map keys)

Note 2: if you are on Java9+, you should use List.of instead of Arrays.asList

Note 3: I haven't checked for neither null nor invalid arguments

Note 4: there is room for improvements, i.e. key extractor functions could be memoized, join keys could be reused instead of calculated more than once and multimap could have Object values for single elements and lists for duplicates, etc

2 of 2
1

If performance and nesting (as discussed) is not too much of a concern you could employ something along the lines of a crossjoin with filtering:

Result holder class

public class Tuple<A, B> {
    public final A a;
    public final B b;

    public Tuple(A a, B b) {
        this.a = a;
        this.b = b;
    }
}

Join with a predicate:

public static <A, B> List<Tuple<A, B>> joinOn(
    List<A> l1,
    List<B> l2,
    Predicate<Tuple<A, B>> predicate) {
    return l1.stream()
        .flatMap(a -> l2.stream().map(b -> new Tuple<>(a, b)))
        .filter(predicate)
        .collect(Collectors.toList());
}

Call it like this:

List<Tuple<NamePropeties, NameEntries>> joined = joinOn(
    properties,
    names,
    t -> Objects.equals(t.a.id, t.b.id) && Objects.equals(t.a.months, t.b.months)
);