В чому різниця між 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. Як правильно працювати з колекціями в незмінних класах]]