CodeGym /Kursy /JAVA 25 SELF /Serializacja kolekcji: List, Map, Set

Serializacja kolekcji: List, Map, Set

JAVA 25 SELF
Poziom 42, Lekcja 4
Dostępny

1. Serializacja kolekcji

Dobra wiadomość: niemal wszystkie standardowe kolekcje Javy (na przykład ArrayList, HashSet, HashMap i spółka) już implementują Serializable. To znaczy, że możesz je serializować od razu, bez skomplikowanych sztuczek.

Przykład:

import java.io.Serializable;
import java.util.ArrayList;

public class MyList extends ArrayList<String> implements Serializable {
    // Można nawet nic nie pisać — ArrayList już jest Serializable!
}

W praktyce zazwyczaj serializujesz standardowe kolekcje i wszystko działa bez zbędnych kłopotów.

Jak serializować kolekcję?

Do serializacji kolekcji używa się tego samego podejścia co dla dowolnego innego obiektu:

  • Tworzymy kolekcję.
  • Zapisujemy ją do pliku przez ObjectOutputStream.
  • Odczytujemy ją z powrotem przez ObjectInputStream.

Przykład 1: Serializacja ArrayList<String>

import java.io.*;
import java.util.*;

public class SerializeListDemo {
    public static void main(String[] args) throws Exception {
        // 1. Tworzymy listę napisów
        List<String> books = new ArrayList<>();
        books.add("Buszujący w zbożu");
        books.add("Wielkie nadzieje");
        books.add("Boska komedia");

        // 2. Zapisujemy listę do pliku
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("books.ser"));
        out.writeObject(books);
        out.close();

        // 3. Odczytujemy listę z pliku
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("books.ser"));
        List<String> loadedBooks = (List<String>) in.readObject();
        in.close();

        // 4. Sprawdzamy wynik
        System.out.println("Lista książek po deserializacji:");
        for (String book : loadedBooks) {
            System.out.println("- " + book);
        }
    }
}

Wynik:

Lista książek po deserializacji:
- Buszujący w zbożu
- Wielkie nadzieje
- Boska komedia

Przykład 2: Serializacja HashSet<Integer>

Set<Integer> numbers = new HashSet<>(Arrays.asList(10, 20, 30, 40));
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("numbers.ser"));
out.writeObject(numbers);
out.close();

ObjectInputStream in = new ObjectInputStream(new FileInputStream("numbers.ser"));
Set<Integer> loadedNumbers = (Set<Integer>) in.readObject();
in.close();

System.out.println("Zbiór po deserializacji: " + loadedNumbers);

Przykład 3: Serializacja HashMap<String, Integer>

Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 100);
scores.put("Bob", 80);

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("scores.ser"));
out.writeObject(scores);
out.close();

ObjectInputStream in = new ObjectInputStream(new FileInputStream("scores.ser"));
Map<String, Integer> loadedScores = (Map<String, Integer>) in.readObject();
in.close();

System.out.println("Mapa po deserializacji: " + loadedScores);

Ważne kwestie

Czy kolejność jest zachowywana?

  • Dla List (np. ArrayList) kolejność elementów jest zawsze zachowywana.
  • Dla Set (HashSet) kolejność nie jest gwarantowana (zarówno przed, jak i po serializacji).
  • Dla Map (HashMap) kolejność par klucz–wartość nie jest gwarantowana (jeśli potrzebujesz kolejności — użyj LinkedHashMap).

Puste kolekcje serializują się i deserializują bez problemów. Po deserializacji otrzymasz pustą listę/zbiór/mapę.

Kolekcje zagnieżdżone (np. List<List<String>>) serializują się poprawnie, jeśli wszystkie zagnieżdżone elementy są serializowalne.

2. Wymagania wobec elementów kolekcji

Tu często czyha pierwsza poważna pułapka!

Wszystkie elementy kolekcji również muszą być serializowalne.

Jeśli choć jeden element kolekcji nie implementuje interfejsu Serializable, serializacja kolekcji zakończy się błędem NotSerializableException. Co więcej, jeśli kolekcja jest duża, a błąd — gdzieś w środku, znalezienie winowajcy zamienia się w pasjonujący „quest”.

Przykład: serializacja kolekcji z nieserializowalnym obiektem

import java.io.*;
import java.util.*;

class NotSerializableClass {
    int value;
    public NotSerializableClass(int value) { 
        this.value = value; 
    }
}

public class NotSerializableDemo {
    public static void main(String[] args) throws Exception {
        List<Object> list = new ArrayList<>();
        list.add("Hello");
        list.add(new NotSerializableClass(123)); // <- Ups!

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("badlist.ser"));
        try {
            out.writeObject(list); // Tutaj zostanie rzucony wyjątek!
        } catch (NotSerializableException e) {
            System.out.println("Błąd serializacji: " + e);
        }
        out.close();
    }
}

Wynik:

Błąd serializacji: java.io.NotSerializableException: NotSerializableClass

Jak sobie z tym poradzić?

To proste: wszystkie elementy kolekcji powinny być albo typami standardowymi, takimi jak String lub Integer, albo Twoimi własnymi klasami, które implementują interfejs Serializable. Jeśli jakaś klasa nie jest serializowalna, wystarczy dodać do niej implements Serializable i problem znika.

3. Specyfika serializacji różnych kolekcji

Czy kolejność elementów jest zachowywana?

  • List: Tak, kolejność elementów jest zachowywana.
  • Set: Zależy od implementacji. W HashSet kolejność nie jest gwarantowana, natomiast w LinkedHashSet — jest zachowywana.
  • Map: W HashMap kolejność nie jest gwarantowana, w LinkedHashMap — zachowywany jest porządek dodawania.

Demonstracja:

List<String> list = Arrays.asList("A", "B", "C");
Set<String> set = new HashSet<>(list);

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("coll.ser"));
out.writeObject(list);
out.writeObject(set);
out.close();

ObjectInputStream in = new ObjectInputStream(new FileInputStream("coll.ser"));
List<String> loadedList = (List<String>) in.readObject();
Set<String> loadedSet = (Set<String>) in.readObject();
in.close();

System.out.println("List: " + loadedList); // zawsze [A, B, C]
System.out.println("Set: " + loadedSet);   // może być [A, C, B] itp.

Serializacja pustych kolekcji

Puste kolekcje serializują się bez problemów. Po deserializacji otrzymasz pusty obiekt wymaganego typu.

Serializacja zagnieżdżonych kolekcji

Można serializować kolekcje, które zawierają inne kolekcje:

List<List<String>> table = new ArrayList<>();
table.add(Arrays.asList("row1-col1", "row1-col2"));
table.add(Arrays.asList("row2-col1", "row2-col2"));

// Serializacja
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("table.ser"));
out.writeObject(table);
out.close();

// Deserializacja
ObjectInputStream in = new ObjectInputStream(new FileInputStream("table.ser"));
List<List<String>> loadedTable = (List<List<String>>) in.readObject();
in.close();

System.out.println(loadedTable);

4. Praktyczny przykład: serializacja kolekcji obiektów własnej klasy

Napiszmy małą aplikację dla „wirtualnej biblioteki”, w której zserializujemy listę książek. Klasa Book musi być serializowalna!

import java.io.*;
import java.util.*;

class Book implements Serializable {
    private static final long serialVersionUID = 1L; // dla zgodności wersji klasy
    String title;
    String author;
    int year;

    public Book(String title, String author, int year) {
        this.title = title;
        this.author = author;
        this.year = year;
    }

    @Override
    public String toString() {
        return title + " (" + author + ", " + year + ")";
    }
}

public class LibraryApp {
    public static void main(String[] args) throws Exception {
        List<Book> books = new ArrayList<>();
        books.add(new Book("Buszujący w zbożu", "J. D. Salinger", 1951));
        books.add(new Book("Wielkie nadzieje", "Charles Dickens", 1861));
        books.add(new Book("Boska komedia", "Dante Alighieri", 1320));

        // Zapisujemy listę do pliku
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("mylibrary.ser"));
        out.writeObject(books);
        out.close();

        // Odczytujemy listę z powrotem
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("mylibrary.ser"));
        List<Book> loadedBooks = (List<Book>) in.readObject();
        in.close();

        System.out.println("Książki z pliku:");
        for (Book b : loadedBooks) {
            System.out.println("- " + b);
        }
    }
}

5. Typowe błędy przy serializacji kolekcji

Błąd nr 1: NotSerializableException na elemencie kolekcji. Jeśli choć jeden element kolekcji nie jest serializowalny, serializacja „wyłoży się” błędem NotSerializableException. Na przykład wtedy, gdy przypadkiem dodasz do kolekcji obiekt, który nie implementuje Serializable, albo zapomnisz dodać ten interfejs do swojej klasy.

Błąd nr 2: Zmiana klasy między serializacją a deserializacją. Jeśli zserializowałeś kolekcję obiektów, a potem zmieniłeś strukturę klasy (np. dodałeś lub usunąłeś pola), przy deserializacji może wystąpić wyjątek InvalidClassException. Aby tego uniknąć, używaj pola serialVersionUID w klasie.

Błąd nr 3: Serializacja kolekcji z polami transient lub static. Pola oznaczone jako transient lub static nie są serializowane. Jeśli Twoje obiekty zależą od takich pól, po deserializacji będą miały wartości domyślne (np. null lub 0).

Błąd nr 4: Serializacja kolekcji ze zagnieżdżonymi nieserializowalnymi obiektami. Jeśli w kolekcji są zagnieżdżone kolekcje lub obiekty, które nie implementują Serializable, błąd pojawi się na najgłębszym poziomie — nie zawsze od razu wiadomo, gdzie szukać problemu. Sprawdzaj wszystkie poziomy zagnieżdżenia!

Błąd nr 5: Wydajność przy dużych kolekcjach. Jeśli kolekcja jest bardzo duża, serializacja może zająć dużo czasu i miejsca na dysku. W takich przypadkach warto rozważyć serializację strumieniową lub podział kolekcji na części.

1
Zadanie
JAVA 25 SELF, poziom 42, lekcja 4
Niedostępne
Serializacja listy napisów
Serializacja listy napisów
1
Zadanie
JAVA 25 SELF, poziom 42, lekcja 4
Niedostępne
Serializacja mapy z ocenami
Serializacja mapy z ocenami
1
Ankieta/quiz
Serializacja, poziom 42, lekcja 4
Niedostępny
Serializacja
Wprowadzenie do serializacji obiektów
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION