Open In App

Collecting a Stream to an Immutable Collection in Java

Last Updated : 25 Oct, 2021
Improve
Improve
Like Article
Like
Save
Share
Report

Streams and Collectors were introduced in Java 8 introduced the concept of Streams. A Stream is a sequence, a sequence of objects. We generate Streams from input sources like Arrays, Lists, etc., and support aggregation operations like filter, map, limit, reduce, etc. We use Streams to pipeline data and ultimately collect it into some form of a Collector. A Collector is nothing but the container used to store the result of the Stream processing. A Collector could be a List, Map, or a Set.

An Immutable collection is a collection whose values we cannot change once created. We do need immutable Collections because many times we can have Collections of data that do not change, i.e., Lookup lists. For example, a list of months in a year or days of the week, etc. Making such lists(which have data that does not change) immutable makes them more memory and space-efficient. They are also inherently thread-safe. Immutable objects, in general, require much less memory than their mutable counterparts.

An object is considered immutable if its state cannot change after it is constructed. After you create an immutable instance of a collection, it holds the same data as long as a reference to it exists.

Methods: Depending on the Java version, multiple methods are available for creating immutable objects as per advancement in versions of java with which we can create Immutable Collections.

  1. Pre-Java 10
    • Creating Stream and collecting it into an unmodifiable structure using the “collectingAndThen
    • Google’s Guava library
  2. After Java 10
    • toUnmodifiableList()
    • toUnmodifiableMap()
    • toUnmodifiableSet()

Scenario 1: Pre-Java 10

Method 1: Creating Stream and collecting it into an unmodifiable structure. Before, Java 10 there was no direct way to create an immutable collection in Java. One method was creating a Stream and collecting it into an unmodifiable structure using the “collectingAndThen” method.

Example:

Java




// Java Program to illustrate Collecting a Stream to an
// Immutable Collection
// Pre java 10
// Using collectingAndThen method
 
// Importing Collections, Collectors and Stream classes
// from java.util package
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.Stream;
 
// Main class
// PreJava10ImmutableCollections
public class GFG {
 
    // Main driver method
    public static void main(String[] args)
    {
 
        // Custom inputs integer elements in List
        var unmodifiableList
            = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
                  .collect(Collectors.collectingAndThen(
                      Collectors.toList(),
                      Collections::unmodifiableList));
 
        System.out.println(unmodifiableList);
 
        // Operations like this will result in an exception
        unmodifiableList.add(12);
    }
}


 Output: 

Method 2: Using Google’s Guava library. For this, a pre-requisite work is required which is to include the JAR file. The JAR for the Guava library is to download the jar and add it to the build path in eclipse. The Guava Library provides the ImmutableList class. An example of the same is given below.

Example:

Java




// Java Program to illustrate Collecting a
// Stream to an Immutable Collection
// Pre java 10  Using Google’s Guava library
 
// Importing Guava library
import com.google.common.collect.ImmutableList;
// Importing classes from java.util package
import java.util.List;
import java.util.stream.IntStream;
 
// Main class
public class PreJava10ImmutableCollections {
 
    // main driver method
    public static void main(String[] args) {
 
        // Using the Guava Libraries
        List<Integer> someList
            = IntStream.range(0, 15).boxed().collect(
                  ImmutableList.toImmutableList());
 
        // Print and display the elements
        System.out.println(someList);
    }
}


Output: 

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

Note: We can also implement our own version of making an immutable list or map or set.

Scenario 2: Java version 10 and above

Java version 10 introduced functions to create Immutable collections through the Collectors class. We have 3 methods, one for List, one for Set, and one for Map. 

Method 1: Using toUnmodifiableList() method. Assume we have a Stream of the first 50 even numbers and create an immutable List from it.

Example: 

Java




// Java Program to illustrate Collecting a Stream to
// an Immutable Collection
// Post java 10
// using toUnmodifiableList() method
 
// Importing classes from java.util package
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
 
// Main Class
// ImmutableCollectionList
public class GFG {
 
    // Main driver method
    public static void main(String[] args)
    {
        // Creating Stream class object of integer type
        Stream<Integer> evenNumberStream
            = Stream.iterate(0, i -> i + 2).limit(50);
 
        // Creating List class object of integer type
        List<Integer> evenNumbers
            = (List<Integer>)evenNumberStream.collect(
                Collectors.toUnmodifiableList());
 
        // Print all elements in the List object
        System.out.println(evenNumbers);
 
        // These will result in
        // java.lang.UnsupportedOperationException
 
        evenNumbers.add(90);
        // evenNumbers.remove(1);
    }
}


Output: 

Method 2: Using toUnmodifiableMap() Method 

Consider an example of a Books class. The Books object has two parameters namely ‘Id’ and ‘Name’. We will generate a Stream of book Objects. Assume that we have to generate an immutable Map from this collection of Objects. To do so, we use the toUnmodifiableMap() function.

Example: 

Java




// Java Program to illustrate Collecting a Stream to
// an Immutable Collection
// Post java 10
// Using toUnmodifiableMap() method
 
// Importing required libraries
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
 
// Main Class
// ImmutableCollectionMap
public class GFG {
 
    // Main driver method
    public static void main(String[] args)
    {
 
        // Creating a Map from a Stream of books by
        // creating an object of List class of books type
        List<Books> libInventory = new ArrayList<Books>();
 
        // Adding elements to the above object created
        // Custom input entries
        libInventory.add(
            new Books(1, "Pride and Prejudice"));
        libInventory.add(new Books(2, "The Sign of Four"));
        libInventory.add(
            new Books(3, "Sense and Sensibility"));
        libInventory.add(new Books(4, "Mansfield Park"));
        libInventory.add(
            new Books(5, "The Materese Circle"));
        libInventory.add(
            new Books(6, "The Hound of Baskerville"));
        libInventory.add(new Books(7, "Goodnight Moon"));
        libInventory.add(new Books(
            8, "How many sleeps till my Birthday"));
        libInventory.add(
            new Books(9, "The Bourne Identity"));
        libInventory.add(new Books(10, "Murder She Wrote"));
        libInventory.add(new Books(
            11, "The adventures of Hercule Poirot"));
        libInventory.add(
            new Books(12, "The song of Ice and Fire"));
 
        // Creating a Map class object
        // Declaring object of integer and string type
        Map<Integer, String> unmutableInventory
            = libInventory.stream().collect(
                Collectors.toUnmodifiableMap(
                    Books::getBookNumber,
                    Books::getBookName));
 
        // Print all the elements in the Map object created
        // above
        System.out.println(unmutableInventory);
 
        // This will result in an Exception
        unmutableInventory.put(13, "Some book");
    }
}


Output:

Method 3: Using toUnmodifiableSet() method 

Implementation: Creating an unmodifiable Set from a stream to do so, we use the toUnmodifiableSet() function. 

Java




// Java Program to illustrate Collecting a Stream to
// an Immutable Collection
// Post java 10
// Using toUnmodifiableSet() method
 
// Importing required libraries
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
 
// Main class
// ImmutableCollectionSet
public class GFG {
 
    // Main driver method
    public static void main(String[] args)
    {
        // Creating Stream object of type Double
        Stream<Double> randomDecimals
            = Stream.generate(Math::random).limit(30);
 
        // Now creating Set class object of type Double
        Set<Double> randomSet = randomDecimals.collect(
            Collectors.toUnmodifiableSet());
 
        // Print and display elements in Set object
        System.out.println(randomSet);
 
        // This will produce an exception
        randomSet.add(100.0);
    }
}


 Conclusion: Here we have seen how to create Immutable Collections from a Stream in Java. We also saw the different methods used to create the Collections based on the Java version being used.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads