How do I use Map.copyOf() for Immutable Maps?

In Java, the Map.copyOf() method, introduced in Java 10, is a factory method used to create an unmodifiable (immutable) copy of a given Map. This method ensures that the resulting Map cannot be modified, and attempting to do so throws an UnsupportedOperationException.

Here’s how to use it effectively:

Syntax:

public static <K,V> Map<K,V> copyOf(Map<? extends K,? extends V> map)
  • Parameters: A single map (Map<? extends K, ? extends V> to copy).
  • Returns: An unmodifiable copy of the given map.
  • Throws:
    • NullPointerException if the provided map or any of its keys/values is null (this method does not allow null as keys or values).
    • IllegalArgumentException if there are duplicate keys in the map.

Examples

1. Creating an Immutable Map from an Existing Map

package org.kodejava.util;

import java.util.Map;

public class MapCopyOfExample {
    public static void main(String[] args) {
        // Original mutable map
        Map<Integer, String> originalMap = Map.of(1, "One", 2, "Two", 3, "Three");

        // Creating an immutable copy
        Map<Integer, String> immutableMap = Map.copyOf(originalMap);

        // Attempting to modify will throw UnsupportedOperationException
        System.out.println(immutableMap); // Output: {1=One, 2=Two, 3=Three}

        // Uncommenting the following line will throw an error
        // immutableMap.put(4, "Four");
    }
}

2. Avoiding Redundant Copies

Map.copyOf() avoids redundant copying. If the input Map is already unmodifiable (e.g., created using Map.copyOf() or Map.of()), it simply returns the same instance.

Map<String, String> immutableMap1 = Map.of("Key1", "Value1", "Key2", "Value2");

// Reusing the immutable instance
Map<String, String> immutableMap2 = Map.copyOf(immutableMap1);

System.out.println(immutableMap1 == immutableMap2); // Output: true

3. Copying a Mutable Map

A mutable map can be made immutable using Map.copyOf().

package org.kodejava.util;

import java.util.HashMap;
import java.util.Map;

public class MutableToImmutable {
    public static void main(String[] args) {
        // Create a mutable map
        Map<String, String> mutableMap = new HashMap<>();
        mutableMap.put("A", "Apple");
        mutableMap.put("B", "Banana");

        // Make an immutable copy
        Map<String, String> immutableMap = Map.copyOf(mutableMap);

        // Attempting to modify the copy throws an exception
        System.out.println(immutableMap); // Output: {A=Apple, B=Banana}

        // mutableMap.put("C", "Cherry"); // Allowed for mutableMap
        // immutableMap.put("C", "Cherry"); // Throws UnsupportedOperationException
    }
}

Notes about Map.copyOf()

  1. Null Values/Keys:
    • Both keys and values must be non-null; otherwise, a NullPointerException will be thrown.
  2. Immutable Nature:
    • The map returned is truly immutable. Not only are modifications disallowed, but if the input map is mutable, changes to the input do not affect the immutable map.
  3. Alternative Methods:
    • Map.of() can also be used to create immutable maps directly, but it requires you to specify the entries upfront.

Key Differences: Map.copyOf() vs Map.of()

Feature Map.copyOf() Map.of()
Input Accepts an existing map Accepts individual key-value pairs
Suitability for Copy Used to copy an existing map Used to create new map
Duplicates Rejects duplicates in the source map Doesn’t allow duplicates
Empty Map Works with an empty map Use Map.of() for emptiness

Summary

The Map.copyOf() method is a lightweight way to create immutable maps, especially from existing maps. It’s perfect for ensuring immutability and avoiding unintentional modifications to data structures. Use it where immutability is key, such as shared configurations, constants, or thread-safe data sharing!

How do I use new Java 10 methods like List.copyOf(), Set.copyOf(), and Map.copyOf()?

Java 10 introduced the List.copyOf(), Set.copyOf(), and Map.copyOf() methods as convenient ways to create unmodifiable copies of existing collections. These methods are part of the java.util package and provide a simpler way to create immutable collections compared to using older methods like Collections.unmodifiableList().

Here’s how you can use them:


1. List.copyOf()

The List.copyOf() method creates an unmodifiable copy of the provided Collection. The returned list:

  • Is immutable (you cannot add, remove, or modify elements).
  • Rejects null elements (throws a NullPointerException).

Example:

package org.kodejava.util;

import java.util.List;

public class ListCopyExample {
    public static void main(String[] args) {
        // Create a mutable list
        List<String> originalList = List.of("A", "B", "C");

        // Create an unmodifiable copy
        List<String> unmodifiableList = List.copyOf(originalList);

        // Print the copied list
        System.out.println(unmodifiableList);

        // Throws UnsupportedOperationException if modification is attempted
        // unmodifiableList.add("D");

        // Throws NullPointerException if original list has nulls
        // List<String> listWithNull = new ArrayList<>();
        // listWithNull.add(null);
        // List.copyOf(listWithNull);
    }
}

2. Set.copyOf()

The Set.copyOf() method creates an unmodifiable copy of the provided Collection, ensuring that:

  • The returned set contains no duplicate elements.
  • Null elements are not allowed.
  • The original collection can be a List, Set, or any Collection.

Example:

package org.kodejava.util;

import java.util.Set;

public class SetCopyExample {
   public static void main(String[] args) {
      // Create a mutable set
      Set<String> originalSet = Set.of("A", "B", "C");

      // Create an unmodifiable copy
      Set<String> unmodifiableSet = Set.copyOf(originalSet);

      // Print the copied set
      System.out.println(unmodifiableSet);

      // Throws UnsupportedOperationException
      // unmodifiableSet.add("D");
   }
}

3. Map.copyOf()

The Map.copyOf() method creates an unmodifiable copy of the provided map. Similar to List.copyOf() and Set.copyOf():

  • The returned map is immutable.
  • Null keys or values are not allowed.
  • Elements retain the original insertion order (if applicable, e.g., for LinkedHashMap).

Example:

package org.kodejava.util;

import java.util.Map;

public class MapCopyExample {
   public static void main(String[] args) {
      // Create a mutable map
      Map<Integer, String> originalMap = Map.of(1, "One", 2, "Two", 3, "Three");

      // Create an unmodifiable copy
      Map<Integer, String> unmodifiableMap = Map.copyOf(originalMap);

      // Print the copied map
      System.out.println(unmodifiableMap);

      // Throws UnsupportedOperationException
      // unmodifiableMap.put(4, "Four");
   }
}

Notes:

  1. Immutable Behavior:
    • Any attempt to modify the unmodifiable collections (e.g., using add() or put()) throws UnsupportedOperationException.
    • These methods return a new collection, but if the input collection is already immutable and meets the conditions, it may return the original collection (performance optimization).
  2. Handling Nulls:
    • If any input collection contains null elements, these methods will throw a NullPointerException.
  3. Differences from Existing Methods:
    • Unlike Collections.unmodifiableList()/Set()/Map(), these methods create a copy, ensuring that changes to the source collection won’t affect the new collection.
  4. Static Imports:
    • These methods belong to static utility classes (List, Set, and Map) and are invoked directly as static methods.

Summary:

  • Use these methods to get immutable copies of collections.
  • They reject null values by design.
  • Collections become unmodifiable and can’t be changed after creation.

They are great for enhancing immutability and safety of the application!