1. Die Methoden copyOf: List.copyOf, Set.copyOf, Map.copyOf
Stellen Sie sich vor: Es gibt eine Methode, die eine Liste, ein Set oder eine Map zurückgeben soll, und es ist wichtig, dass der aufrufende Code den Inhalt dieser Collection in keiner Weise verändern kann. Zum Beispiel „Liste der unterstützten Währungen“ oder „Satz der Benutzerrollen“. Jede versehentliche Einfügung/Löschung kann die Geschäftslogik verletzen.
Vor Java 10 musste man entweder die Collection manuell kopieren oder Wrapper wie Collections.unmodifiableList(list) verwenden. Ein Wrapper schützt nur vor Änderungen über sich selbst, aber nicht vor Modifikationen der Ausgangs-Collection.
Jetzt gibt es eine robustere Lösung – die Methoden copyOf.
Was ist das?
Die Methoden List.copyOf(Collection), Set.copyOf(Collection), Map.copyOf(Map) (Java 10) erstellen eine echte, unveränderliche Kopie der übergebenen Collection bzw. Map.
- Die Kopie ist nicht mit der Original-Collection verknüpft: Änderungen am Original beeinflussen das Ergebnis von copyOf nicht.
- Die Kopie ist nicht veränderbar: Jeder Änderungsversuch führt zu UnsupportedOperationException.
Beispiel für das Erstellen einer unveränderlichen Kopie
import java.util.*;
public class CopyOfDemo {
public static void main(String[] args) {
List<String> modifiable = new ArrayList<>();
modifiable.add("Java");
modifiable.add("Python");
// Erstellen wir eine unveränderliche Kopie
List<String> immutable = List.copyOf(modifiable);
// Versuchen wir, die Kopie zu ändern
try {
immutable.add("C++"); // Wirft UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.out.println("Es ist nicht möglich, ein Element zu einer unveränderlichen Sammlung hinzuzufügen!");
}
// Wir ändern die Ursprungsliste
modifiable.add("Kotlin");
// Die Kopie blieb unverändert!
System.out.println("Ursprüngliche Liste: " + modifiable);
System.out.println("Unveränderliche Kopie: " + immutable);
}
}
Programmausgabe:
Es ist nicht möglich, ein Element zu einer unveränderlichen Sammlung hinzuzufügen!
Ursprüngliche Liste: [Java, Python, Kotlin]
Unveränderliche Kopie: [Java, Python]
Kurz zu jeder Methode
- List.copyOf(Collection) – gibt eine unveränderliche Liste mit den Elementen der ursprünglichen Collection zurück.
- Set.copyOf(Collection) – gibt ein unveränderliches Set mit den Elementen der Collection zurück; Duplikate werden entfernt.
- Map.copyOf(Map) – gibt eine unveränderliche Map mit den Paaren aus der ursprünglichen Map zurück.
Beispiel mit Set und Map
import java.util.*;
public class CopyOfSetMapDemo {
public static void main(String[] args) {
Set<String> modifiableSet = new HashSet<>(Set.of("A", "B", "C"));
Set<String> immutableSet = Set.copyOf(modifiableSet);
// Versuch, ein Element hinzuzufügen
try {
immutableSet.add("D");
} catch (UnsupportedOperationException e) {
System.out.println("Das Set kann nicht geändert werden!");
}
Map<String, Integer> modifiableMap = new HashMap<>();
modifiableMap.put("Alice", 30);
modifiableMap.put("Bob", 25);
Map<String, Integer> immutableMap = Map.copyOf(modifiableMap);
try {
immutableMap.put("Charlie", 28);
} catch (UnsupportedOperationException e) {
System.out.println("Die Map kann nicht geändert werden!");
}
}
}
2. Besonderheiten und Einschränkungen der Methoden copyOf
Null-Elemente sind nicht erlaubt
Wie die Fabrikmethoden List.of, Set.of, Map.of verbieten die Methoden copyOf null als Element, Schlüssel oder Wert. Befindet sich in der ursprünglichen Collection/Map ein null, wird beim Kopieren eine NullPointerException ausgelöst.
List<String> listWithNull = Arrays.asList("A", null, "B");
List<String> immutable = List.copyOf(listWithNull); // Wirft NullPointerException!
Die Collection ist tatsächlich unveränderlich
Jeder Versuch von add/remove/put/replace führt zu UnsupportedOperationException.
Original und Kopie sind nicht gekoppelt
Änderungen an der ursprünglichen Collection wirken sich nicht auf die Kopie aus – und umgekehrt.
Wenn die Collection bereits unveränderlich ist – wird dieselbe Instanz zurückgegeben
Wird bereits eine unveränderliche Collection übergeben (z. B. das Ergebnis von List.of(...)), gibt copyOf dasselbe Objekt zurück (Speicherersparnis).
List<String> immutable = List.of("X", "Y");
List<String> copy = List.copyOf(immutable);
System.out.println(immutable == copy); // true
Konkrete Implementierung ist nicht garantiert
Der Rückgabetyp der Collection ist lediglich List, Set oder Map. Es handelt sich nicht zwingend um ArrayList, HashSet oder HashMap. Verlassen Sie sich nicht auf Implementierungsdetails (z. B. interner Typ und Optimierungen); zugesichert sind Unveränderlichkeit und der jeweilige Interface-Vertrag.
3. Unterschied zwischen copyOf und Wrappern (unmodifiable wrappers)
Collections.unmodifiableList(list) erzeugt einen Wrapper über einer bestehenden Collection. Wenn die ursprüngliche Collection verändert wird, sind diese Änderungen auch im Wrapper sichtbar.
List.copyOf(list) erzeugt eine neue, unveränderliche Collection, die nicht mit dem Original verbunden ist.
Demonstration des Unterschieds
import java.util.*;
public class WrapperVsCopyOf {
public static void main(String[] args) {
List<String> original = new ArrayList<>(List.of("A", "B"));
// Wrapper
List<String> wrapper = Collections.unmodifiableList(original);
// Kopie
List<String> copy = List.copyOf(original);
// Wir ändern das Original
original.add("C");
System.out.println("Wrapper: " + wrapper); // [A, B, C]
System.out.println("Kopie: " + copy); // [A, B]
}
}
Ausgabe:
Wrapper: [A, B, C]
Kopie: [A, B]
Der Wrapper spiegelt die Änderungen des Originals wider, die Kopie bleibt unverändert.
4. Praktische Einsatzszenarien für copyOf
Schutz der Daten bei der Rückgabe aus einer Methode
public class CurrencyService {
private final List<String> currencies = new ArrayList<>(List.of("USD", "EUR", "JPY"));
public List<String> getSupportedCurrencies() {
// Niemand kann das Ergebnis verändern
return List.copyOf(currencies);
}
}
Jetzt kann der aufrufende Code keine Währung zum zurückgegebenen Liste hinzufügen oder daraus entfernen.
Übergabe zwischen den Schichten der Anwendung
Wenn Daten zwischen Schichten (DAO → Service → Controller) übergeben werden, verwenden Sie copyOf, um sicherzugehen, dass niemand die Collection „auf dem Weg“ ändert.
Sichere Veröffentlichung in nebenläufigen Programmen
Unveränderliche Collections sind eine bequeme Möglichkeit, Daten zwischen Threads ohne zusätzliche Synchronisierung zu teilen.
5. Typische Fehler bei der Verwendung von copyOf und unveränderlichen Collections
Fehler Nr. 1: Versuch, null einzufügen. Die Methoden copyOf (wie auch of) tolerieren kein null – weder als Element einer Liste/eines Sets noch als Schlüssel/Wert einer Map. Beim Kopieren erhalten Sie eine NullPointerException.
Fehler Nr. 2: Verwechslung zwischen Wrapper und Kopie. copyOf erstellt eine unabhängige Kopie: Änderungen am Original sind in der Kopie nicht sichtbar. Collections.unmodifiableList(list) hingegen umhüllt lediglich die Ausgangs-Collection: Alle Änderungen am Original werden im Wrapper sichtbar.
Fehler Nr. 3: Versuch, eine unveränderliche Collection zu ändern. Aufrufe von add/remove/put bei Collections aus copyOf/of führen zu UnsupportedOperationException.
Fehler Nr. 4: Einschränkungen der Map-Fabriken vergessen. Zum Beispiel unterstützt Map.of() bis zu 10 Schlüssel–Wert-Paare. Wenn Sie mehr benötigen – verwenden Sie Map.ofEntries(...) oder bauen Sie eine veränderliche Map und wenden anschließend Map.copyOf an.
Fehler Nr. 5: Sich auf eine konkrete Implementierung verlassen. Erwarten Sie nicht, dass copyOf genau eine ArrayList oder ein HashSet zurückgibt. Garantiert sind nur das Interface (List/Set/Map) und die Unveränderlichkeit.
GO TO FULL VERSION