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.
GO TO FULL VERSION