CodeGym /Kursy /JAVA 25 SELF /Map.copyOf, Set.copyOf i inne narzędzia

Map.copyOf, Set.copyOf i inne narzędzia

JAVA 25 SELF
Poziom 34, Lekcja 1
Dostępny

1. Metody copyOf: List.copyOf, Set.copyOf, Map.copyOf

Wyobraź sobie sytuację: jest metoda, która ma zwrócić listę, zbiór lub mapę, a jednocześnie ważne jest, aby kod wywołujący w żaden sposób nie mógł zmienić zawartości tej kolekcji. Na przykład „lista obsługiwanych walut” lub „zestaw ról użytkownika”. Jakiekolwiek przypadkowe wstawienie/usunięcie może naruszyć logikę biznesową.

Przed Java 10 trzeba było albo ręcznie zwracać kopię kolekcji, albo używać opakowań typu Collections.unmodifiableList(list). Opakowanie chroni tylko przed zmianami przez nie samo, ale nie przed modyfikacjami kolekcji źródłowej.

Teraz mamy bardziej niezawodny sposób – metody copyOf.

Co to takiego?

Metody List.copyOf(Collection), Set.copyOf(Collection), Map.copyOf(Map) (Java 10) tworzą prawdziwą niemodyfikowalną kopię przekazanej kolekcji lub mapy.

  • Kopia nie jest powiązana z kolekcją źródłową: zmiany oryginału nie wpływają na wynik copyOf.
  • Zmiana kopii jest niemożliwa: każda modyfikacja spowoduje UnsupportedOperationException.

Przykład utworzenia niemodyfikowalnej kopii

import java.util.*;

public class CopyOfDemo {
    public static void main(String[] args) {
        List<String> modifiable = new ArrayList<>();
        modifiable.add("Java");
        modifiable.add("Python");

        // Tworzymy niemodyfikowalną kopię
        List<String> immutable = List.copyOf(modifiable);

        // Spróbujmy zmienić kopię
        try {
            immutable.add("C++"); // Wyrzuci UnsupportedOperationException
        } catch (UnsupportedOperationException e) {
            System.out.println("Nie można dodać elementu do niemodyfikowalnej kolekcji!");
        }

        // Zmieniamy listę źródłową
        modifiable.add("Kotlin");

        // Kopia pozostała niezmieniona!
        System.out.println("Lista źródłowa: " + modifiable);
        System.out.println("Niemodyfikowalna kopia: " + immutable);
    }
}

Wynik programu:

Nie można dodać elementu do niemodyfikowalnej kolekcji!
Lista źródłowa: [Java, Python, Kotlin]
Niemodyfikowalna kopia: [Java, Python]

Krótko o poszczególnych metodach

  • List.copyOf(Collection) – zwraca niemodyfikowalną listę z elementami z kolekcji źródłowej.
  • Set.copyOf(Collection) – zwraca niemodyfikowalny zbiór z elementami z kolekcji; duplikaty zostaną usunięte.
  • Map.copyOf(Map) – zwraca niemodyfikowalną mapę z parami z mapy źródłowej.

Przykład z Set i 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);

        // Spróbujmy dodać element
        try {
            immutableSet.add("D");
        } catch (UnsupportedOperationException e) {
            System.out.println("Nie można zmienić Set!");
        }

        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("Nie można zmienić Map!");
        }
    }
}

2. Cechy i ograniczenia metod copyOf

Elementy null są niedozwolone

Podobnie jak metody fabryczne List.of, Set.of, Map.of, metody copyOf zabraniają null jako elementu, klucza lub wartości. Jeśli w kolekcji/mapie źródłowej znajduje się null, podczas kopiowania zostanie zgłoszony NullPointerException.

List<String> listWithNull = Arrays.asList("A", null, "B");
List<String> immutable = List.copyOf(listWithNull); // Zgłosi NullPointerException!

Kolekcja jest rzeczywiście niemodyfikowalna

Każda próba add/remove/put/replace prowadzi do UnsupportedOperationException.

Oryginał i kopia nie są powiązane

Zmiany w kolekcji źródłowej nie wpływają na kopię i odwrotnie.

Jeśli kolekcja jest już niemodyfikowalna – zwracany jest ten sam obiekt

Jeśli przekażesz już niemodyfikowalną kolekcję (np. wynik List.of(...)) – copyOf zwróci ten sam obiekt (oszczędność pamięci).

List<String> immutable = List.of("X", "Y");
List<String> copy = List.copyOf(immutable);

System.out.println(immutable == copy); // true

Konkretna implementacja nie jest gwarantowana

Typ zwracanej kolekcji to po prostu List, Set lub Map. To nie musi być ArrayList, HashSet ani HashMap. Nie polegaj na detalach implementacyjnych (np. wewnętrzny typ i optymalizacje); kontrakt to niemodyfikowalność i odpowiedni interfejs.

3. Różnica między copyOf a opakowaniami (unmodifiable wrappers)

Collections.unmodifiableList(list) tworzy opakowanie na istniejącej kolekcji. Jeśli kolekcja źródłowa się zmienia, te zmiany są widoczne także w opakowaniu.

List.copyOf(list) tworzy nową, niemodyfikowalną kolekcję, niepowiązaną z oryginałem.

Demonstracja różnicy

import java.util.*;

public class WrapperVsCopyOf {
    public static void main(String[] args) {
        List<String> original = new ArrayList<>(List.of("A", "B"));

        // Opakowanie
        List<String> wrapper = Collections.unmodifiableList(original);

        // Kopia
        List<String> copy = List.copyOf(original);

        // Zmieńmy oryginał
        original.add("C");

        System.out.println("Opakowanie: " + wrapper); // [A, B, C]
        System.out.println("Kopia: " + copy);          // [A, B]
    }
}

Wynik:

Opakowanie: [A, B, C]
Kopia: [A, B]

Opakowanie odzwierciedla zmiany oryginału, a kopia pozostaje niezmieniona.

4. Praktyczne scenariusze użycia copyOf

Ochrona danych przy zwracaniu z metody

public class CurrencyService {
    private final List<String> currencies = new ArrayList<>(List.of("USD", "EUR", "JPY"));

    public List<String> getSupportedCurrencies() {
        // Nikt nie będzie mógł zmienić wyniku
        return List.copyOf(currencies);
    }
}

Teraz kod wywołujący nie będzie mógł dodać ani usunąć waluty z zwracanej listy.

Przekazywanie między warstwami aplikacji

Gdy dane są przekazywane między warstwami (DAO → serwis → kontroler), używaj copyOf, aby mieć pewność, że nikt nie zmieni kolekcji „po drodze”.

Bezpieczna publikacja w programach wielowątkowych

Niemodyfikowalne kolekcje to wygodny sposób, by bezpiecznie udostępniać dane między wątkami bez dodatkowej synchronizacji.

5. Typowe błędy przy używaniu copyOf i kolekcji niemodyfikowalnych

Błąd nr 1: próba wstawienia null. Metody copyOf (tak jak of) nie tolerują null – ani jako elementu listy/zbioru, ani jako klucza/wartości mapy. Podczas kopiowania otrzymasz NullPointerException.

Błąd nr 2: mylenie opakowania z kopią. copyOf tworzy niezależną kopię: zmiany oryginału nie są widoczne w kopii. Natomiast Collections.unmodifiableList(list) jedynie opakowuje kolekcję źródłową: wszystkie zmiany oryginału będą odzwierciedlane w opakowaniu.

Błąd nr 3: próba zmiany kolekcji niemodyfikowalnej. Wywołania add/remove/put na kolekcjach z copyOf/of prowadzą do UnsupportedOperationException.

Błąd nr 4: zapomniane ograniczenia metod fabrycznych map. Na przykład Map.of() obsługuje do 10 par klucz–wartość. Jeśli potrzebujesz więcej – użyj Map.ofEntries(...) albo zbuduj modyfikowalną mapę, a następnie zastosuj Map.copyOf.

Błąd nr 5: poleganie na konkretnej implementacji. Nie oczekuj, że copyOf zwróci konkretnie ArrayList lub HashSet. Gwarantowane są jedynie interfejs (List/Set/Map) i niemodyfikowalność.

1
Zadanie
JAVA 25 SELF, poziom 34, lekcja 1
Niedostępne
Koszyk sklepu: widok na żywo kontra migawka 🛒
Koszyk sklepu: widok na żywo kontra migawka 🛒
1
Zadanie
JAVA 25 SELF, poziom 34, lekcja 1
Niedostępne
Konfiguracja gry: utrwalamy ustawienia wydania 🎮
Konfiguracja gry: utrwalamy ustawienia wydania 🎮
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION