IT TIP

전체 복사, 얕은 복사, 복제

itqueen 2020. 10. 23. 19:47
반응형

전체 복사, 얕은 복사, 복제


Java에서 딥 복사, 얕은 복사 및 복제의 차이점에 대한 설명이 필요합니다.


불행히도 "shallow copy", "deep copy"및 "clone"은 모두 다소 잘못 정의 된 용어입니다.


Java 컨텍스트에서 먼저 "값 복사"와 "객체 복사"를 구분해야합니다.

int a = 1;
int b = a;     // copying a value
int[] s = new int[]{42};
int[] t = s;   // copying a value (the object reference for the array above)

StringBuffer sb = new StringBuffer("Hi mom");
               // copying an object.
StringBuffer sb2 = new StringBuffer(sb);

간단히 말해서, 유형이 참조 유형 인 변수에 대한 참조 할당은 값이 객체 참조 인 "값 복사"입니다. 객체를 복사하려면 new명시 적으로 또는 내부적으로 를 사용해야 합니다.


이제 개체의 "얕은"대 "깊은"복사에 대해 설명합니다. 얕은 복사는 일반적으로 개체의 한 수준 만 복사하는 것을 의미하고, 전체 복사는 일반적으로 두 개 이상의 수준을 복사하는 것을 의미합니다. 문제는 우리가 의미하는 수준을 결정하는 것입니다. 이걸 고려하세요:

public class Example {
    public int foo;
    public int[] bar;
    public Example() { };
    public Example(int foo, int[] bar) { this.foo = foo; this.bar = bar; };
}

Example eg1 = new Example(1, new int[]{1, 2});
Example eg2 = ... 

정상적인 해석은의 "얕은"복사본 이 1과 같고 필드가 원본과 동일한 배열을 참조 eg1하는 새 Example개체 가된다는 것입니다 . 예 :foobar

Example eg2 = new Example(eg1.foo, eg1.bar);

"깊은"사본의 일반적인 해석 은 1과 같고 필드 가 원래 배열 사본을 참조 eg1하는 새 Example객체입니다 . 예 :foobar

Example eg2 = new Example(eg1.foo, Arrays.copy(eg1.bar));

(C / C ++ 배경에서 온 사람들 은 참조 할당이 얕은 사본을 생성한다고 말할 수 있습니다 . 그러나 이는 일반적으로 Java 컨텍스트에서 얕은 복사를 의미하는 것이 아닙니다 ...)

두 가지 추가 질문 / 불확실성 영역이 있습니다.

  • 얼마나 깊습니까? 두 단계에서 멈추나요? 3 단계? 연결된 개체의 전체 그래프를 의미합니까?

  • 캡슐화 된 데이터 유형은 어떻습니까? 예 : 문자열? String은 실제로 하나의 객체가 아닙니다. 사실, 일부 스칼라 필드와 문자 배열에 대한 참조가있는 "객체"입니다. 그러나 문자 배열은 API에 의해 완전히 숨겨져 있습니다. 그래서 우리가 문자열 복사에 대해 이야기 할 때, 그것을 "얕은"복사본 또는 "깊은"복사본이라고 부르는 것이 합리적입니까? 아니면 그냥 사본이라고 불러야할까요?


마지막으로 복제합니다. Clone은 일반적으로 대상 개체의 복사본을 생성하는 것으로 생각되는 모든 클래스 (및 배열)에 존재하는 메서드입니다. 하나:

  • 이 방법의 사양은 이것이 얕은 복사본인지 깊은 복사본인지 (의미있는 구별이라고 가정) 고의적으로 말하지 않습니다.

  • 실제로 사양에는 클론이 새 객체를 생성한다고 구체적으로 명시되어 있지 않습니다.

다음 은 javadoc이 말하는 내용입니다.

"이 객체의 사본을 생성하고 반환합니다."복사 "의 정확한 의미는 객체의 클래스에 따라 달라질 수 있습니다. 일반적인 의도는 모든 객체 x에 대해 표현식 x.clone() != x이 참이고 표현식 x.clone().getClass() == x.getClass()이 참 이라는 것입니다. , 그러나 이는 절대적인 요구 사항은 아닙니다. 일반적으로 x.clone().equals(x)사실 인 경우이지만 절대적인 요구 사항 은 아닙니다. "

이것은 한 극단에서 복제본 대상 개체 있고 다른 극단에서 복제본 원본과 같지 않을 수 있음을 의미 합니다. 그리고 이것은 클론이 지원되는 것으로 가정합니다.

간단히 말해 복제는 잠재적으로 모든 Java 클래스에 대해 다른 것을 의미합니다.


어떤 사람들은 (@supercat이 주석에서하는 것처럼) Java clone()메서드가 손상 되었다고 주장 합니다. 하지만 정확한 결론은 OO의 맥락에서 클론개념 이 깨 졌다는 것입니다. AFAIK, 모든 개체 유형에서 일관성 있고 사용할 수있는 통합 복제 모델을 개발하는 것은 불가능합니다.


"복제"라는 용어는 모호하며 (Java 클래스 라이브러리에 Cloneable 인터페이스가 포함되어 있음 ) 전체 복사 또는 단순 복사를 참조 할 수 있습니다. 딥 / 얕은 복사본은 특별히 Java와 관련이 없지만 개체의 복사본을 만드는 것과 관련된 일반적인 개념이며 개체의 구성원도 복사되는 방법을 나타냅니다.

예를 들어 사람 클래스가 있다고 가정 해 보겠습니다.

class Person {
    String name;
    List<String> emailAddresses
}

이 클래스의 객체를 어떻게 복제합니까? 단순 복사를 수행하는 경우 이름을 복사 emailAddresses하고 새 객체에 참조를 넣을 수 있습니다 . 그러나 emailAddresses목록 의 내용을 수정 한 경우 두 복사본 모두에서 목록을 수정하게됩니다 (개체 참조가 작동하는 방식이므로).

전체 복사는 모든 멤버를 재귀 적으로 복사하는 것을 의미하므로 new List에 대한 새 항목 Person을 만든 다음 이전 항목에서 새 개체로 내용을 복사해야합니다.

Although the above example is trivial, the differences between deep and shallow copies are significant and have a major impact on any application, especially if you are trying to devise a generic clone method in advance, without knowing how someone might use it later. There are times when you need deep or shallow semantics, or some hybrid where you deep copy some members but not others.


  • Deep copy: Clone this object and every reference to every other object it has
  • Shallow copy: Clone this object and keep its references
  • Object clone() throws CloneNotSupportedException: It is not specified whether this should return a deep or shallow copy, but at the very least: o.clone() != o

The terms "shallow copy" and "deep copy" are a bit vague; I would suggest using the terms "memberwise clone" and what I would call a "semantic clone". A "memberwise clone" of an object is a new object, of the same run-time type as the original, for every field, the system effectively performs "newObject.field = oldObject.field". The base Object.Clone() performs a memberwise clone; memberwise cloning is generally the right starting point for cloning an object, but in most cases some "fixup work" will be required following a memberwise clone. In many cases attempting to use an object produced via memberwise clone without first performing the necessary fixup will cause bad things to happen, including the corruption of the object that was cloned and possibly other objects as well. Some people use the term "shallow cloning" to refer to memberwise cloning, but that's not the only use of the term.

A "semantic clone" is an object which is contains the same data as the original, from the point of view of the type. For examine, consider a BigList which contains an Array> and a count. A semantic-level clone of such an object would perform a memberwise clone, then replace the Array> with a new array, create new nested arrays, and copy all of the T's from the original arrays to the new ones. It would not attempt any sort of deep-cloning of the T's themselves. Ironically, some people refer to the of cloning "shallow cloning", while others call it "deep cloning". Not exactly useful terminology.

While there are cases where truly deep cloning (recursively copying all mutable types) is useful, it should only be performed by types whose constituents are designed for such an architecture. In many cases, truly deep cloning is excessive, and it may interfere with situations where what's needed is in fact an object whose visible contents refer to the same objects as another (i.e. a semantic-level copy). In cases where the visible contents of an object are recursively derived from other objects, a semantic-level clone would imply a recursive deep clone, but in cases where the visible contents are just some generic type, code shouldn't blindly deep-clone everything that looks like it might possibly be deep-clone-able.

참고URL : https://stackoverflow.com/questions/6182565/deep-copy-shallow-copy-clone

반응형