JavaRush /Курсы /JAVA 25 SELF /Сериализация коллекций: List, Map, Set

Сериализация коллекций: List, Map, Set

JAVA 25 SELF
42 уровень , 4 лекция
Открыта

1. Сериализация коллекций

Хорошая новость: почти все стандартные коллекции Java (например, ArrayList, HashSet, HashMap и их друзья) уже реализуют Serializable. Это значит, что вы можете сериализовать их прямо из коробки, без сложных танцев с бубном.

Пример:

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

public class MyList extends ArrayList<String> implements Serializable {
    // Можно даже ничего не писать — ArrayList уже Serializable!
}

На практике вы обычно сериализуете стандартные коллекции, и всё работает без лишних хлопот.

Как сериализовать коллекцию?

Для сериализации коллекции используется тот же подход, что и для любого другого объекта:

  • Создаём коллекцию.
  • Записываем её в файл через ObjectOutputStream.
  • Читаем обратно через ObjectInputStream.

Пример 1: Сериализация ArrayList<String>

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

public class SerializeListDemo {
    public static void main(String[] args) throws Exception {
        // 1. Создаём список строк
        List<String> books = new ArrayList<>();
        books.add("Над пропастью во ржи");
        books.add("Большие надежды");
        books.add("Божественная комедия");

        // 2. Сохраняем список в файл
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("books.ser"));
        out.writeObject(books);
        out.close();

        // 3. Читаем список обратно из файла
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("books.ser"));
        List<String> loadedBooks = (List<String>) in.readObject();
        in.close();

        // 4. Проверяем результат
        System.out.println("Список книг после десериализации:");
        for (String book : loadedBooks) {
            System.out.println("- " + book);
        }
    }
}

Вывод:

Список книг после десериализации:
- Над пропастью во ржи
- Большие надежды
- Божественная комедия

Пример 2: Сериализация 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("Множество после десериализации: " + loadedNumbers);

Пример 3: Сериализация 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("Карта после десериализации: " + loadedScores);

Важные моменты

Сохраняется ли порядок?

  • Для List (например, ArrayList) порядок элементов всегда сохраняется.
  • Для Set (HashSet) порядок не гарантируется (и до, и после сериализации).
  • Для Map (HashMap) порядок пар ключ-значение не гарантируется (если нужен порядок — используйте LinkedHashMap).

Пустые коллекции сериализуются и десериализуются без проблем. После десериализации вы получите пустой список/множество/карту.

Вложенные коллекции (например, List<List<String>>) сериализуются корректно, если все вложенные элементы сериализуемы.

2. Требования к элементам коллекции

Вот тут часто поджидает первая серьёзная ловушка!

Все элементы коллекции тоже должны быть сериализуемыми.

Если хотя бы один элемент коллекции не реализует интерфейс Serializable, сериализация коллекции завершится ошибкой NotSerializableException. Причём, если коллекция большая, а ошибка — где-то внутри, поиск виновника превращается в увлекательный квест.

Пример: сериализация коллекции с несериализуемым объектом

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)); // <- Опа!

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("badlist.ser"));
        try {
            out.writeObject(list); // Здесь будет выброшено исключение!
        } catch (NotSerializableException e) {
            System.out.println("Ошибка сериализации: " + e);
        }
        out.close();
    }
}

Результат:

Ошибка сериализации: java.io.NotSerializableException: NotSerializableClass

Как быть?

Бороться с этим просто: все элементы коллекции должны быть либо стандартными типами, такими как String или Integer, либо вашими собственными классами, которые реализуют интерфейс Serializable. Если какой-то класс не сериализуем, достаточно добавить к нему implements Serializable, и проблема решится.

3. Особенности сериализации разных коллекций

Сохраняется ли порядок элементов?

  • List: Да, порядок элементов сохраняется.
  • Set: Зависит от реализации. В HashSet порядок не гарантируется, а вот в LinkedHashSet — сохраняется.
  • Map: В HashMap порядок не гарантируется, в LinkedHashMap — сохраняется порядок добавления.

Демонстрация:

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); // всегда [A, B, C]
System.out.println("Set: " + loadedSet);   // может быть [A, C, B] и т.д.

Сериализация пустых коллекций

Пустые коллекции сериализуются без проблем. После десериализации вы получите пустой объект нужного типа.

Сериализация вложенных коллекций

Можно сериализовать коллекции, которые содержат другие коллекции:

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

// Сериализация
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("table.ser"));
out.writeObject(table);
out.close();

// Десериализация
ObjectInputStream in = new ObjectInputStream(new FileInputStream("table.ser"));
List<List<String>> loadedTable = (List<List<String>>) in.readObject();
in.close();

System.out.println(loadedTable);

4. Практический пример: сериализация коллекции объектов собственного класса

Давайте вместе напишем маленькое приложение для «виртуальной библиотеки», где будем сериализовать список книг. Класс Book должен быть сериализуемым!

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

class Book implements Serializable {
    private static final long serialVersionUID = 1L; // для совместимости версий класса
    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("Над пропастью во ржи", "Дж. Д. Сэлинджер", 1951));
        books.add(new Book("Большие надежды", "Чарльз Диккенс", 1861));
        books.add(new Book("Божественная комедия", "Данте Алигьери", 1320));

        // Сохраняем список в файл
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("mylibrary.ser"));
        out.writeObject(books);
        out.close();

        // Читаем список обратно
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("mylibrary.ser"));
        List<Book> loadedBooks = (List<Book>) in.readObject();
        in.close();

        System.out.println("Книги из файла:");
        for (Book b : loadedBooks) {
            System.out.println("- " + b);
        }
    }
}

5. Типичные ошибки при сериализации коллекций

Ошибка №1: NotSerializableException на элементе коллекции. Если хотя бы один элемент коллекции несериализуем, сериализация «завалится» с ошибкой NotSerializableException. Например, если вы случайно добавили в коллекцию объект, который не реализует Serializable, или забыли добавить этот интерфейс в свой класс.

Ошибка №2: Изменение класса между сериализацией и десериализацией. Если вы сериализовали коллекцию объектов, а потом поменяли структуру класса (например, добавили или удалили поля), при десериализации может возникнуть исключение InvalidClassException. Чтобы избежать этого, используйте поле serialVersionUID в классе.

Ошибка №3: Сериализация коллекции с transient или static полями. Поля, помеченные как transient или static, не сериализуются. Если ваши объекты зависят от таких полей, после десериализации они будут иметь значения по умолчанию (например, null или 0).

Ошибка №4: Сериализация коллекций с вложенными несериализуемыми объектами. Если в коллекции есть вложенные коллекции или объекты, не реализующие Serializable, ошибка возникнет на самом глубоком уровне — не всегда сразу понятно, где искать проблему. Проверяйте все уровни вложенности!

Ошибка №5: Производительность при больших коллекциях. Если коллекция очень большая, сериализация может занять много времени и места на диске. В таких случаях стоит задуматься о потоковой сериализации или разбивке коллекции на части.

1
Задача
JAVA 25 SELF, 42 уровень, 4 лекция
Недоступна
Сериализация списка строк
Сериализация списка строк
1
Задача
JAVA 25 SELF, 42 уровень, 4 лекция
Недоступна
Сериализация карты с баллами
Сериализация карты с баллами
1
Опрос
Сериализация, 42 уровень, 4 лекция
Недоступен
Сериализация
Введение в сериализацию объектов
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ