← Назад к вопросам

В чем разница между deepCopy и shallowCopy?

2.0 Middle🔥 71 комментариев
#JVM и память#Коллекции и структуры данных

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Различие между Deep Copy и Shallow Copy

В объектно-ориентированном программировании, особенно при работе с Android на Kotlin/Java, понимание разницы между глубоким (deep copy) и поверхностным (shallow copy) копированием критически важно для управления памятью, избежания ошибок и обеспечения корректной работы приложения.

Основная концепция

Поверхностное копирование создает новый объект, но копирует только ссылки на вложенные объекты, а не сами объекты. Оба объекта (оригинал и копия) разделяют одни и те же вложенные объекты.

Глубокое копирование создает полностью независимую копию, включая все вложенные объекты, рекурсивно копируя всю иерархию объектов.

Технические различия

Поверхностное копирование (Shallow Copy)

data class Person(
    val name: String,
    val address: Address
)

data class Address(
    val city: String,
    val street: String
)

fun main() {
    val originalAddress = Address("Москва", "Тверская")
    val originalPerson = Person("Иван", originalAddress)
    
    // Поверхностное копирование
    val shallowCopy = originalPerson.copy()
    
    // Изменение вложенного объекта отразится на обоих
    originalAddress.city = "Санкт-Петербург"
    
    println(originalPerson.address.city) // Санкт-Петербург
    println(shallowCopy.address.city)    // Санкт-Петербург
}

Глубокое копирование (Deep Copy)

data class PersonDeep(
    val name: String,
    val address: AddressDeep
) {
    fun deepCopy(): PersonDeep {
        return PersonDeep(
            name = this.name,
            address = this.address.copy() // Копируем вложенный объект
        )
    }
}

data class AddressDeep(
    val city: String,
    val street: String
)

fun main() {
    val originalAddress = AddressDeep("Москва", "Тверская")
    val originalPerson = PersonDeep("Иван", originalAddress)
    
    // Глубокое копирование
    val deepCopy = originalPerson.deepCopy()
    
    // Изменение вложенного объекта НЕ отразится на копии
    originalAddress.city = "Санкт-Петербург"
    
    println(originalPerson.address.city) // Санкт-Петербург
    println(deepCopy.address.city)       // Москва
}

Ключевые различия в Android-контексте

Использование памяти

  • Shallow copy более эффективен по памяти, так как не дублирует вложенные объекты
  • Deep copy потребляет больше памяти, но обеспечивает полную независимость

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

  • Поверхностное копирование выполняется быстрее (O(1) для простых случаев)
  • Глубокое копирование может быть медленным для сложных объектных графов (O(n), где n - количество объектов)

Безопасность и побочные эффекты

  • Shallow copy может привести к неожиданным побочным эффектам при изменении общих объектов
  • Deep copy предотвращает побочные эффекты, обеспечивая полную изоляцию

Практическое применение в Android

Когда использовать Shallow Copy:

  • Неизменяемые (immutable) объекты
  • Временные копии для чтения данных
  • Ситуации, когда разделение объектов допустимо
  • Оптимизация производительности критичных участков

Когда использовать Deep Copy:

  • Работа с изменяемыми (mutable) состояниями
  • Сохранение снимков состояния (snapshots)
  • Передача данных между компонентами с гарантией неизменности
  • Сериализация/десериализация объектов
  • Многопоточное программирование (во избежание race conditions)

Особенности реализации в Kotlin

В Kotlin data class автоматически генерирует метод copy(), который является поверхностным:

// Автоматически сгенерированный copy() делает shallow copy
val copied = original.copy()

Для реализации глубокого копирования обычно:

  1. Реализуют метод deepCopy() вручную
  2. Используют сериализацию (Gson, Moshi, kotlinx.serialization)
  3. Применяют библиотеки типа MapStruct или ручное копирование
// Пример с сериализацией для deep copy
import com.google.gson.Gson

fun <T> T.deepCopy(): T {
    val json = Gson().toJson(this)
    return Gson().fromJson(json, this::class.java)
}

Риски и лучшие практики

Основные риски shallow copy:

  • Непреднамеренное изменение общих данных
  • Проблемы в многопоточных средах
  • Сложность отладки из-за неочевидных связей

Рекомендации:

  • По умолчанию предпочитайте неизменяемые структуры данных
  • Документируйте тип копирования в публичных API
  • Используйте глубокое копирование для передачи данных между слоями приложения
  • Для сложных объектов реализуйте явные методы deepCopy() с четкой документацией

Выбор между deep и shallow copy зависит от конкретного контекста: требований к производительности, необходимости изоляции данных и семантики предметной области. В Android-разработке особенно важно учитывать эти различия при работе с ViewModels, LiveData, StateFlow и другими компонентами архитектуры, где неправильное копирование может привести к трудноуловимым багам и утечкам памяти.