Convert Java 8 Streams to Collections

Process data using Java 8 Streams and store in Collections including List, Set or Map.

“If we knew what it was we were doing, it would not be called research, would it?”
― Albert Einstein

1. Introduction

Java 8 Streams provide a cool facility to combine several operations in a single statement and express it concisely. Once the data is processed, you can collect the data in a List (or another collection). In this article, we present several methods to collect the results of these stream operations into collections.

2. Convert Stream to a List

Here is a simple example. Stream.of() creates a stream from the names which is collected in a list.

List<String> alist = Stream.of("Sarah",
                               "Ariana",
                               "Liliana",
                               "Clara",
                               "Mariah",
                               "Naomi",
                               "Bailey")
    .collect(Collectors.toList());
System.out.println(alist);
# prints
[Sarah, Ariana, Liliana, Clara, Mariah, Naomi, Bailey]

3. Stream to List: Specify the List Type

The method Collectors.toList() returns a List whose actual implementation is unknown. What if you want to collect the items in a particular type of list, say a LinkedList? Here is how you can do it?

List<String> alist = Stream.of("Sarah",
                               "Ariana",
                               "Liliana",
                               "Clara",
                               "Mariah",
                               "Naomi",
                               "Bailey")
    .collect(Collectors.toCollection(LinkedList::new));

Would you like to convert the Stream to a plain array instead of a Collection? Use toArray() as follows:

String[] arr = Stream.of("Sarah",
                         "Ariana",
                         "Liliana",
                         "Clara",
                         "Mariah",
                         "Naomi",
                         "Bailey")
    .toArray(String[]::new);
System.out.println("t3: {" +Arrays.stream(arr).collect(Collectors.joining(", "))+"}");
# prints
t3: {Sarah, Ariana, Liliana, Clara, Mariah, Naomi, Bailey}

4. Convert Stream to Set

Using the toSet() method is straightforward enough to convert a Stream to a Set.

Set<String> aset = Stream.of("Sarah",
                             "Ariana",
                             "Liliana",
                             "Clara",
                             "Mariah",
                             "Naomi",
                             "Bailey")
    .collect(Collectors.toSet());
System.out.println(aset);
# prints
[Naomi, Liliana, Mariah, Sarah, Clara, Ariana, Bailey]

And as before, to specify the type of the Set, use toCollection().

HashSet<String> aset = Stream.of("Sarah",
                                 "Ariana",
                                 "Liliana",
                                 "Clara",
                                 "Mariah",
                                 "Naomi",
                                 "Bailey")
    .collect(Collectors.toCollection(HashSet::new));

5. Convert Stream to Map

Converting data from a Stream to a Map is a bit more involved since you need a key mapper function and a value mapper function.

In the following example, we read a CSV file of first-name frequencies by state, apply a filter for the year 2010 and the state of CA, and collect the results in a map of Name => Frequency.

String fileName = "StateNames.csv";
Pattern pattern = Pattern.compile(",");
try (BufferedReader in = new BufferedReader(new FileReader(fileName));){
    Map<String,Integer> amap = in
        .lines()
        .skip(1)
        .map(line -> pattern.split(line))
        .filter(x -> x[2].equals("2010") &&
                x[4].equals("CA") &&
                Integer.parseInt(x[5]) > 50)
        .collect(Collectors.toMap(x -> x[1] + "(" + x[3] + ")",
                                  x -> new Integer(x[5])));
    System.out.println(amap);
}
# prints
...
Shaun(M)=54, Arturo(M)=217, Moises(M)=209, Malachi(M)=161, Maverick(M)=62, Erica(F)=70, Julieta(F)=80
...

To specify the type of the Map to use, you need to specify a merge function and a map supplier function.

The merge function takes two values and returns a single value. It is used when there are multiple mappings for the same key. In our case, we know we won’t have collisions because the names are unique for a state, so we use a dummy function that just returns the first value.

...
(x, y) -> x
...

And for the map supplier function, we use the constructor reference for HashMap.

...
HashMap::new
...

The whole invocation now looks like this, and the output is a HashMap.

    Map<String,Integer> amap = in
        .lines()
        .skip(1)
        .map(line -> pattern.split(line))
        .filter(x -> x[2].equals("2010") &&
                x[4].equals("CA") &&
                Integer.parseInt(x[5]) > 50)
        .collect(Collectors.toMap(x -> x[1] + "(" + x[3] + ")",
                                  x -> new Integer(x[5]),
                                  (x, y) -> x,
                                  HashMap::new));

Review

We presented some ways of converting a java 8 Stream to several collection types, including a List, Set and Map.