В чем разница между глубокой копией и мелкой копией?
В чем разница между глубокой копией и мелкой копией?
Ответы (30)
Мелкие копии как можно меньше дублируют. Неглубокая копия коллекции - это копия структуры коллекции, а не элементов. С мелкой копией две коллекции теперь разделяют отдельные элементы.
Глубокие копии все дублируют. Глубокая копия коллекции - это две коллекции, в которых все элементы исходной коллекции продублированы.
int[]
в Java, элементы действительно будут скопированы.
- person aioobe; 19.03.2018
Customer
, который имеет объект Address
, побитовое копирование объекта Customer
означает, что копируется указатель / ссылка на объект Address
. Оригинал и копия указывают на один и тот же объект Address
, тогда как глубокая копия создаст новый объект Address
и будет указывать на него.
- person Raphael Schmitz; 21.02.2019
Ширина vs глубина; представьте себе дерево ссылок с вашим объектом в качестве корневого узла.
Мелкий:
Переменные A и B относятся к разным областям памяти, когда B назначается A, две переменные относятся к одной и той же области памяти. Более поздние изменения содержимого одного из них мгновенно отражаются на содержимом другого, поскольку они совместно используют содержимое.
Глубокий:
Переменные A и B относятся к различным областям памяти, когда B назначается A, значения в области памяти, на которую указывает A, копируются в область памяти, на которую указывает B. Более поздние модификации содержимого остаются уникальными для A или B; содержимое не передается.
Короче говоря, это зависит от того, что на что указывает. В неглубокой копии объект B указывает на местоположение объекта A в памяти. При глубоком копировании все вещи в ячейке памяти объекта A копируются в ячейку памяти объекта B.
В этой вики-статье есть отличная диаграмма.
http://en.wikipedia.org/wiki/Object_copy
Попробуйте рассмотреть следующее изображение
Например, Object.MemberwiseClone создает мелкую копию ссылка
а с помощью интерфейса ICloneable вы можете получить глубокую копию, как описано здесь
Специально для разработчиков iOS:
Если B
является мелкой копией A
, то для примитивных данных это похоже на B = [A assign];
, а для объектов это похоже на B = [A retain]
;
B и A указывают на одну и ту же ячейку памяти
Если B
является глубокой копией A
, то это похоже на B = [A copy];
B и A указывают на разные участки памяти
Адрес памяти B такой же, как у A
B имеет то же содержимое, что и A
Неглубокая копия: копирует значения членов из одного объекта в другой.
Глубокое копирование: копирует значения членов из одного объекта в другой.
Любые объекты-указатели дублируются и копируются глубоко.
Пример:
class String
{
int size;
char* data;
};
String s1("Ace"); // s1.size = 3 s1.data=0x0000F000
String s2 = shallowCopy(s1);
// s2.size =3 s2.data = 0X0000F000
String s3 = deepCopy(s1);
// s3.size =3 s3.data = 0x0000F00F
// (With Ace copied to this location.)
Для простоты понимания вы можете прочитать эту статью: https://www.cs.utexas.edu/~scottm/cs307/handouts/deepCopying.htm
Мелкая копия:
Глубокая копия:
Я не видел здесь короткого, простого для понимания ответа, поэтому я попробую.
При неглубокой копии любой объект, на который указывает источник, также указывается адресатом (так что никакие ссылочные объекты не копируются).
При глубокой копии любой объект, на который указывает источник, копируется, а копия указывается адресатом (так что теперь будет 2 объекта каждого ссылочного объекта). Это повторяется вниз по дереву объектов.
{Представьте два объекта: A и B одного типа _t (по отношению к C ++), и вы думаете о поверхностном / глубоком копировании A в B}
Неглубокая копия: просто копирует ссылку на А в Б. Думайте об этом как о копии адреса А. Таким образом, адреса A и B будут одинаковыми, то есть они будут указывать на одно и то же место в памяти, то есть на содержимое данных.
Глубокая копия: просто делает копию всех членов A, выделяет память в другом месте для B, а затем назначает скопированные элементы B для получения глубокой копии. Таким образом, если A становится несуществующим, B все еще действует в памяти. Правильный термин для использования - клонирование, когда вы знаете, что они оба полностью одинаковы, но все же разные (т.е. хранятся как два разных объекта в пространстве памяти). Вы также можете предоставить свою оболочку клона, где через список включения / исключения вы можете решить, какие свойства выбрать во время глубокого копирования. Это довольно распространенная практика при создании API.
Вы можете сделать мелкую копию ТОЛЬКО_Если вы понимаете, на какие ставки это нужно. Когда у вас есть огромное количество указателей на C ++ или C, делать неглубокую копию объекта - ДЕЙСТВИТЕЛЬНО плохая идея.
EXAMPLE_OF_DEEP COPY_ Например, когда вы пытаетесь выполнить обработку изображений и распознавание объектов, вам необходимо замаскировать «нерелевантное и повторяющееся движение» из областей обработки. Если вы используете указатели изображений, у вас может быть спецификация для сохранения этих изображений-масок. СЕЙЧАС ... если вы делаете неглубокую копию изображения, когда ссылки указателя УБИВАЮТСЯ из стека, вы потеряли ссылку и ее копию, т.е. в какой-то момент произойдет ошибка времени выполнения, связанная с нарушением доступа. В этом случае вам нужна глубокая копия вашего изображения путем КЛОНИРОВАНИЯ. Таким образом, вы можете получить маски, если они вам понадобятся в будущем.
EXAMPLE_OF_SHALLOW_COPY Я не очень хорошо осведомлен по сравнению с пользователями в StackOverflow, поэтому не стесняйтесь удалить эту часть и привести хороший пример, если вы можете уточнить. Но я действительно думаю, что делать неглубокое копирование - не лучшая идея, если вы знаете, что ваша программа будет работать в течение бесконечного периода времени, то есть непрерывную операцию «push-pop» над стеком с вызовами функций. Если вы демонстрируете что-то любителю или новичку (например, учебные материалы по C / C ++), это, вероятно, нормально. Но если вы используете такое приложение, как система наблюдения и обнаружения или система слежения за сонаром, вы не должны постоянно копировать свои объекты, потому что это рано или поздно убьет вашу программу.
char * Source = "Hello, world.";
char * ShallowCopy = Source;
char * DeepCopy = new char(strlen(Source)+1);
strcpy(DeepCopy,Source);
«ShallowCopy» указывает на то же место в памяти, что и «Источник». «DeepCopy» указывает на другое место в памяти, но содержимое то же самое.
Что такое мелкая копия?
Неглубокая копия - это побитовая копия объекта. Создается новый объект, который имеет точную копию значений в исходном объекте. Если какое-либо из полей объекта является ссылками на другие объекты, копируются только адреса ссылок, т.е. копируется только адрес памяти.
На этом рисунке MainObject1
имеет поля field1
типа int и ContainObject1
типа ContainObject
. Когда вы делаете неглубокую копию MainObject1
, создается MainObject2
с field2
, содержащим скопированное значение field1
и по-прежнему указывающим на сам ContainObject1
. Обратите внимание, что, поскольку field1
имеет примитивный тип, его значение копируется в field2
, но поскольку ContainedObject1
является объектом, MainObject2
по-прежнему указывает на ContainObject1
. Таким образом, любые изменения, внесенные в ContainObject1
в MainObject1
, будут отражены в MainObject2
.
Теперь, если это мелкая копия, давайте посмотрим, что такое глубокая копия?
Что такое Deep Copy?
Глубокая копия копирует все поля и делает копии динамически выделяемой памяти, на которую указывают поля. Глубокая копия происходит, когда объект копируется вместе с объектами, на которые он ссылается.
На этом рисунке MainObject1 имеет поля field1
типа int и ContainObject1
типа ContainObject
. Когда вы делаете глубокую копию MainObject1
, создается MainObject2
с field2
, содержащим скопированное значение field1
и ContainObject2
, содержащим скопированное значение ContainObject1
. Обратите внимание, что любые изменения, внесенные в ContainObject1
в MainObject1
, не будут отражены в MainObject2
.
field3
, который, когда вы в состоянии попытаться понять что-то столь же глубокое, как эта проблема, где это №3 в этом примере имеет место ContainObject2
?
- person Robb_2015; 29.12.2015
В объектно-ориентированном программировании тип включает в себя набор полей-членов. Эти поля могут храниться либо по значению, либо по ссылке (т. Е. Указатель на значение).
В мелкой копии создается новый экземпляр типа, а значения копируются в новый экземпляр. Ссылочные указатели также копируются точно так же, как и значения. Следовательно, ссылки указывают на исходные объекты. Любые изменения в элементах, которые хранятся по ссылке, появляются как в оригинале, так и в копии, поскольку копия объекта, на который имеется ссылка, не была сделана.
В глубокой копии поля, которые хранятся по значению, копируются, как и раньше, но указатели на объекты, хранящиеся по ссылке, не копируются. Вместо этого делается глубокая копия указанного объекта и сохраняется указатель на новый объект. Любые изменения, внесенные в указанные объекты, не повлияют на другие копии объекта.
«ShallowCopy» указывает на то же место в памяти, что и «Источник». «DeepCopy» указывает на другое место в памяти, но содержимое то же самое.
Неглубокое клонирование:
Определение: «Неглубокая копия объекта копирует« основной »объект, но не копирует внутренние объекты». Когда пользовательский объект (например, Employee) имеет только примитивные переменные типа String, тогда вы используете Shallow Cloning.
Employee e = new Employee(2, "john cena");
Employee e2=e.clone();
Вы возвращаете super.clone();
в переопределенном методе clone (), и ваша работа завершена.
Глубокое клонирование:
Определение: «В отличие от неглубокой копии, глубокая копия является полностью независимой копией объекта».
Означает, что объект Employee содержит другой настраиваемый объект:
Employee e = new Employee(2, "john cena", new Address(12, "West Newbury", "Massachusetts");
Затем вам нужно написать код для клонирования объекта Address, а также в переопределенном методе clone (). В противном случае объект Address не будет клонироваться, и это вызовет ошибку при изменении значения Address в клонированном объекте Employee, который также отражает исходный.
Я хотел бы привести пример, а не формальное определение.
var originalObject = {
a : 1,
b : 2,
c : 3,
};
Этот код показывает неглубокую копию:
var copyObject1 = originalObject;
console.log(copyObject1.a); // it will print 1
console.log(originalObject.a); // it will also print 1
copyObject1.a = 4;
console.log(copyObject1.a); //now it will print 4
console.log(originalObject.a); // now it will also print 4
var copyObject2 = Object.assign({}, originalObject);
console.log(copyObject2.a); // it will print 1
console.log(originalObject.a); // it will also print 1
copyObject2.a = 4;
console.log(copyObject2.a); // now it will print 4
console.log(originalObject.a); // now it will print 1
Этот код показывает глубокую копию:
var copyObject2 = Object.assign({}, originalObject);
console.log(copyObject2.a); // it will print 1
console.log(originalObject.a); // it will also print 1
copyObject2.a = 4;
console.log(copyObject2.a); // now it will print 4
console.log(originalObject.a); // !! now it will print 1 !!
1 1 4 4 4 4 4 4
- person Suresh Prajapati; 11.08.2019
Source deep ${source.c.age}
) source.c.age = 3 console.log (dCopy.c.age) console.log (Source deep ${source.c.age}
)
- person Swarup Chavan; 29.07.2020
Глубокая копия
Глубокая копия копирует все поля и делает копии динамически выделяемой памяти, на которую указывают поля. Глубокая копия происходит, когда объект копируется вместе с объектами, на которые он ссылается.
Неглубокая копия
Неглубокая копия - это побитовая копия объекта. Создается новый объект, который имеет точную копию значений в исходном объекте. Если какое-либо из полей объекта является ссылками на другие объекты, копируются только адреса ссылок, т.е. копируется только адрес памяти.
Неглубокая копия - ссылочная переменная внутри исходного и неглубокого скопированных объектов ссылается на общий объект.
Глубокая копия - ссылочная переменная внутри исходного и глубоко скопированного объектов ссылается на другой объект.
clone всегда делает мелкую копию.
public class Language implements Cloneable{
String name;
public Language(String name){
this.name=name;
}
public String getName() {
return name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
основной класс следующий-
public static void main(String args[]) throws ClassNotFoundException, CloneNotSupportedException{
ArrayList<Language> list=new ArrayList<Language>();
list.add(new Language("C"));
list.add(new Language("JAVA"));
ArrayList<Language> shallow=(ArrayList<Language>) list.clone();
//We used here clone since this always shallow copied.
System.out.println(list==shallow);
for(int i=0;i<list.size();i++)
System.out.println(list.get(i)==shallow.get(i));//true
ArrayList<Language> deep=new ArrayList<Language>();
for(Language language:list){
deep.add((Language) language.clone());
}
System.out.println(list==deep);
for(int i=0;i<list.size();i++)
System.out.println(list.get(i)==deep.get(i));//false
}
OutPut из вышеперечисленного будет-
ложь правда правда
ложно ложно ложно
Любое изменение, сделанное в исходном объекте, отразится на мелком объекте, а не на глубоком.
list.get(0).name="ViSuaLBaSiC";
System.out.println(shallow.get(0).getName()+" "+deep.get(0).getName());
OutPut- ViSuaLBaSiC C
Проще говоря, неглубокая копия аналогична вызову по ссылке, а глубокая копия аналогична вызову по значению.
При вызове по ссылке как формальные, так и фактические параметры функции относятся к одной и той же ячейке памяти и значению.
При вызове по значению как формальные, так и фактические параметры функции относятся к разным ячейкам памяти, но имеют одинаковое значение.
Представьте, что есть два массива с именами arr1 и arr2.
arr1 = arr2; //shallow copy
arr1 = arr2.clone(); //deep copy
Неглубокая копия создает новый составной объект и вставляет в него ссылки на исходный объект.
В отличие от мелкой копии, глубокая копия создает новый составной объект, а также вставляет копии исходных объектов исходного составного объекта.
Возьмем пример.
import copy
x =[1,[2]]
y=copy.copy(x)
z= copy.deepcopy(x)
print(y is z)
Над кодом печатается ЛОЖЬ.
Посмотрим как.
Исходный составной объект x=[1,[2]]
(называется составным, потому что у него есть объект внутри объекта (Начало))
как вы можете видеть на изображении, внутри списка есть список.
Затем мы создаем его неглубокую копию с помощью y = copy.copy(x)
. Что здесь делает python, так это то, что он создает новый составной объект, но объекты внутри него указывают на исходные объекты.
На изображении он создал новую копию для внешнего списка. но внутренний список остается таким же, как и исходный.
Теперь мы создаем его глубокую копию, используя z = copy.deepcopy(x)
. что здесь делает python, он создаст новый объект для внешнего списка, а также для внутреннего списка. как показано на изображении ниже (выделено красным).
В конце код печатает False
, поскольку y и z не являются одними и теми же объектами.
HTH.
Чтобы добавить больше к другим ответам,
- Неглубокая копия объекта выполняет копирование по значению для свойств на основе типов значений и копирование по ссылке для свойств на основе ссылочных типов.
- Глубокая копия объекта выполняет копирование по значению для свойств на основе типов значений, а также копирование по значению для свойств на основе ссылочных типов глубоко в иерархии (ссылочных типов)
Неглубокое копирование - это создание нового объекта, а затем копирование нестатических полей текущего объекта в новый объект. Если поле имеет тип значения -> выполняется побитовое копирование поля; для ссылочного типа -> ссылка копируется, а упомянутый объект - нет; следовательно, исходный объект и его клон относятся к одному и тому же объекту.
Глубокая копия - это создание нового объекта, а затем копирование нестатических полей текущего объекта в новый объект. Если поле имеет тип значения -> выполняется побитовое копирование поля. Если поле имеет ссылочный тип -> создается новая копия указанного объекта. Клонируемые классы должны быть помечены как [Serializable].
Взято из [блога]: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html.
Глубокая копия предполагает использование содержимого одного объекта для создания другого экземпляра того же класса. В глубокой копии два объекта могут содержать одну и ту же информацию, но целевой объект будет иметь свои собственные буферы и ресурсы. разрушение любого объекта не повлияет на оставшийся объект. Перегруженный оператор присваивания создает глубокую копию объектов.
Неглубокая копия включает в себя копирование содержимого одного объекта в другой экземпляр того же класса, что создает зеркальное отображение. Благодаря прямому копированию ссылок и указателей два объекта будут совместно использовать одно и то же внешнее содержимое другого объекта, что будет непредсказуемо.
Объяснение:
Используя конструктор копирования, мы просто копируем значения данных член за членом. Этот метод копирования называется мелким копированием. Если объект является простым классом, состоящим из встроенных типов и без указателей, это было бы приемлемо. Эта функция будет использовать значения и объекты, и ее поведение не будет изменено мелкой копией, копируются только адреса указателей, которые являются членами, а не значение, на которое указывает адрес. Значения данных объекта тогда будут непреднамеренно изменены функцией. Когда функция выходит за пределы области видимости, копия объекта со всеми его данными извлекается из стека.
Если у объекта есть указатели, необходимо выполнить глубокую копию. При глубокой копии объекта память выделяется для объекта в свободном хранилище, и элементы, на которые указываются, копируются. Глубокая копия используется для объектов, возвращаемых функцией.
Я пришел к пониманию из следующих строк.
Неглубокая копия копирует поля типа значения (int, float, bool) объекта в целевой объект, а ссылочные типы объекта (строка, класс и т. Д.) Копируются как ссылки в целевой объект. . В этом целевые ссылочные типы будут указывать на ячейку памяти исходного объекта.
Глубокая копия копирует значение объекта и ссылочные типы в полную новую копию целевых объектов. Это означает, что и типам значений, и ссылочным типам будут выделены новые ячейки памяти.
Неглубокая копия не создаст новую ссылку, но глубокая копия создаст новую ссылку.
Вот программа, объясняющая глубокую и поверхностную копию.
public class DeepAndShollowCopy {
int id;
String name;
List<String> testlist = new ArrayList<>();
/*
// To performing Shallow Copy
// Note: Here we are not creating any references.
public DeepAndShollowCopy(int id, String name, List<String>testlist)
{
System.out.println("Shallow Copy for Object initialization");
this.id = id;
this.name = name;
this.testlist = testlist;
}
*/
// To performing Deep Copy
// Note: Here we are creating one references( Al arraylist object ).
public DeepAndShollowCopy(int id, String name, List<String> testlist) {
System.out.println("Deep Copy for Object initialization");
this.id = id;
this.name = name;
String item;
List<String> Al = new ArrayList<>();
Iterator<String> itr = testlist.iterator();
while (itr.hasNext()) {
item = itr.next();
Al.add(item);
}
this.testlist = Al;
}
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Oracle");
list.add("C++");
DeepAndShollowCopy copy=new DeepAndShollowCopy(10,"Testing", list);
System.out.println(copy.toString());
}
@Override
public String toString() {
return "DeepAndShollowCopy [id=" + id + ", name=" + name + ", testlist=" + testlist + "]";
}
}
Копирование архивов:
Array - это класс, что означает, что это ссылочный тип, поэтому array1 = array2 приводит к двум переменным, которые ссылаются на один и тот же массив.
Но посмотрите на этот пример:
static void Main()
{
int[] arr1 = new int[] { 1, 2, 3, 4, 5 };
int[] arr2 = new int[] { 6, 7, 8, 9, 0 };
Console.WriteLine(arr1[2] + " " + arr2[2]);
arr2 = arr1;
Console.WriteLine(arr1[2] + " " + arr2[2]);
arr2 = (int[])arr1.Clone();
arr1[2] = 12;
Console.WriteLine(arr1[2] + " " + arr2[2]);
}
мелкое клонирование означает, что копируется только память, представленная клонированным массивом.
Если массив содержит объекты типа значения, значения копируются;
если массив содержит ссылочный тип, копируются только ссылки - в результате получается два массива, члены которых ссылаются на одни и те же объекты.
Чтобы создать глубокую копию, где тип ссылки дублируется, вы должны пройти через массив и клонировать каждый элемент вручную.
private void button1_Click(object sender, EventArgs e) { int[] arr1 = new int[]{1,2,3,4,5}; int[] arr2 = new int[]{6,7,8,9,0}; MessageBox.Show(arr1[2] + " " + arr2[2]); arr2 = arr1; MessageBox.Show(arr1[2] + " " + arr2[2]); arr1[2] = 12; MessageBox.Show(arr1[2] + " " + arr2[2]); }
- person DeanOC; 20.12.2012
В дополнение ко всем вышеперечисленным определениям еще одна и наиболее часто используемая глубокая копия находится в конструкторе копирования (или операторе перегрузки присваивания) класса.
Неглубокая копия -> - это когда вы не предоставляете конструктор копирования. Здесь копируется только объект, но не все члены класса копируются.
Глубокая копия -> - это когда вы решили реализовать конструктор копирования или присвоение перегрузки в своем классе и позволяет копировать все члены класса.
MyClass& MyClass(const MyClass& obj) // copy constructor for MyClass
{
// write your code, to copy all the members and return the new object
}
MyClass& operator=(const MyClass& obj) // overloading assignment operator,
{
// write your code, to copy all the members and return the new object
}
Конструктор копирования используется для инициализации нового объекта ранее созданным объектом того же класса. По умолчанию компилятор написал неглубокую копию. Неглубокая копия работает нормально, когда динамическое выделение памяти не задействовано, потому что, когда задействовано динамическое выделение памяти, оба объекта будут указывать на одно и то же место памяти в куче. Поэтому для устранения этой проблемы мы написали глубокую копию, чтобы у обоих объектов была собственная копия атрибутов. в памяти. Чтобы прочитать подробности с полными примерами и пояснениями, вы можете прочитать статью Конструкторы C ++ .
Чтобы добавить еще немного для путаницы между мелкой копией и просто назначить новое имя переменной для списка.
"Допустим, у нас есть:
x = [
[1,2,3],
[4,5,6],
]
Этот оператор создает 3 списка: 2 внутренних списка и один внешний список. Затем ссылка на внешний список становится доступной под именем x. Если мы сделаем
y = x
никакие данные не копируются. У нас где-то в памяти остались те же 3 списка. Все, что это сделало, - это сделало внешний список доступным под именем y в дополнение к его предыдущему имени x. Если мы сделаем
y = list(x)
or
y = x[:]
Это создает новый список с тем же содержанием, что и x. Список x содержит ссылку на 2 внутренних списка, поэтому новый список также будет содержать ссылку на те же 2 внутренних списка. Копируется только один список - внешний список. Теперь в памяти 4 списка: два внутренних списка, внешний список и копия внешнего списка. Исходный внешний список доступен под именем x, а новый внешний список доступен под именем y.
Внутренние списки не копировались! На этом этапе вы можете получить доступ к внутренним спискам и отредактировать их по x или y!
Если у вас есть двумерный (или более высокий) список или какая-либо вложенная структура данных, и вы хотите сделать полную копию всего, тогда вы хотите использовать функцию deepcopy () в модуле копирования. Ваше решение также работает для двумерных списков, поскольку выполняет итерацию по элементам внешнего списка и делает копию каждого из них, а затем создает новый внешний список для всех внутренних копий ».