Вопрос 14 · Раздел 13

В чём разница между shallow copy и deep copy?

на другие объекты (коллекция, массив, любой класс с полями-ссылками), но элементы внутри остаются теми же (те же ссылки).

Версии по языкам: English Russian Ukrainian

Junior Level

Shallow Copy (Поверхностная копия) — копируется только сам контейнер = объект, содержащий ссылки на другие объекты (коллекция, массив, любой класс с полями-ссылками), но элементы внутри остаются теми же (те же ссылки).

Deep Copy (Глубокая копия) — копируется контейнер и все объекты внутри него.

Пример

class User {
    String name;
    User(String name) { this.name = name; }
}

List<User> original = new ArrayList<>();
original.add(new User("Ivan"));

// Shallow Copy
List<User> shallow = new ArrayList<>(original);

// Теперь оба списка ссылаются на одного и того же User:
shallow.get(0).name = "Petr";
System.out.println(original.get(0).name); // "Petr" — изменился и в оригинале!

Аналогия

  • Shallow Copy — сделать фотокопию коробки, но оставить содержимое общим
  • Deep Copy — сделать фотокопию коробки и скопировать каждую вещь внутри

Middle Level

Shallow Copy

  • Копируются: примитивные поля + ссылки на объекты
  • Оригинал и копия делят одни и те же вложенные объекты
  • Механизмы: new ArrayList<>(original), array.clone()
  • Object.clone() — по умолчанию делает shallow copy. Для deep copy нужно переопределить clone() и вручную клонировать вложенные объекты.

Deep Copy

  • Копируются: всё, включая вложенные объекты (рекурсивно)
  • Оригинал и копия полностью независимы
  • Механизмы:
    1. Конструкторы копирования — в каждом классе конструктор, принимающий объект того же типа
    2. Сериализация — через ObjectOutputStream / ObjectInputStream
    3. JSON — сериализация в JSON и обратно
    4. Библиотеки — Apache Commons SerializationUtils.clone()

Пример Deep Copy

public class User implements Cloneable {
    String name;
    Address address; // мутабельный объект

    @Override
    public User clone() {
        User u = new User();
        u.name = this.name;              // String иммутабелен
        u.address = this.address.clone(); // глубокая копия
        return u;
    }
}

Когда что использовать

Сценарий Shallow Deep
Коллекция String / Integer Достаточно Избыточно
Коллекция мутабельных объектов Опасно Обязательно
Сложность O(1) или O(n) для контейнера O(n × depth) — рекурсивно по всему графу

Senior Level

Shallow Immutability — источник скрытых багов

Поверхностная иммутабельность — самая частая причина багов:

public record Config(Map<String, User> users) {
    public Config {
        users = Map.copyOf(users); // Shallow — пользователи те же
    }
}
// Кто-то делает: config.users().get("admin").setRole("superadmin")

Производительность Deep Copy

  • $O(n \times m)$ — где n = размер контейнера, m = глубина графа
  • Сериализация: очень медленно, создаёт много объектов
  • Конструкторы копирования: быстро, но много ручного кода

Structural Sharing

Персистентные структуры данных (Vavr, PCollections) используют structural sharing — при изменении копируется только путь к элементу, а остальные ветви общие. Это даёт $O(\log n)$ вместо $O(n)$.

Резюме для Senior

  • Shallow Copy — копирует “верхушку айсберга”; подходит для коллекций иммутабельных объектов
  • Deep Copy — копирует всю структуру; необходима для истинной иммутабельности
  • Всегда уточняйте глубину копирования при проектировании API
  • Для больших графов рассмотрите персистентные структуры с structural sharing

🎯 Шпаргалка для интервью

Обязательно знать:

  • Shallow Copy — копируется контейнер, элементы те же (те же ссылки)
  • Deep Copy — копируется контейнер и все вложенные объекты рекурсивно
  • Shallow достаточно для коллекций String/Integer; Deep обязателен для мутабельных объектов
  • Механизмы Deep Copy: конструкторы копирования, сериализация, JSON, библиотеки
  • Object.clone() — по умолчанию shallow; для deep нужно переопределить
  • Structural Sharing (Vavr) — $O(\log n)$ вместо $O(n)$ при копировании
  • Shallow Immutability — источник скрытых багов: контейнер неизменен, элементы mutable

Частые уточняющие вопросы:

  • Когда Shallow Copy опасна? — Когда коллекция содержит мутабельные объекты
  • Deep Copy — какие механизмы? — Конструкторы копирования (быстро), сериализация (медленно), JSON
  • Что такое Structural Sharing? — При изменении копируется только путь, остальные ветви общие (Vavr)
  • Object.clone() делает deep copy? — Нет, по умолчанию shallow; нужно переопределить

Красные флаги (НЕ говорить):

  • «Shallow Copy = полная копия» — только контейнер, элементы те же
  • «Object.clone() по умолчанию deep» — нет, shallow
  • «Deep Copy всегда нужен» — для String/Integer коллекций избыточен
  • «Сериализация — быстрый способ» — очень медленно, много объектов

Связанные темы:

  • [[8. Достаточно ли сделать все поля final для иммутабельности]]
  • [[10. Что такое defensive copy (защитная копия)]]
  • [[12. Как защитить коллекцию от изменений]]
  • [[24. Что такое persistent data structures]]
  • [[29. Как правильно работать с коллекциями в иммутабельных классах]]