В чём разница между shallow copy и deep copy?
на другие объекты (коллекция, массив, любой класс с полями-ссылками), но элементы внутри остаются теми же (те же ссылки).
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
- Копируются: всё, включая вложенные объекты (рекурсивно)
- Оригинал и копия полностью независимы
- Механизмы:
- Конструкторы копирования — в каждом классе конструктор, принимающий объект того же типа
- Сериализация — через
ObjectOutputStream/ObjectInputStream - JSON — сериализация в JSON и обратно
- Библиотеки — 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. Как правильно работать с коллекциями в иммутабельных классах]]