it-swarm-ko.tech

Java로 객체의 딥 카피를 만드는 방법은 무엇입니까?

Java에서는 딥 객체 복사 기능을 구현하는 것이 약간 어렵습니다. 원본 개체와 복제 된 개체가 참조를 공유하지 않도록하기 위해 취하는 조치는 무엇입니까?

273
Andrei Savu

안전한 방법은 객체를 직렬화 한 다음 비 직렬화하는 것입니다. 이것은 모든 것이 새로운 참조임을 보장합니다.

여기에 기사가 있습니다 이 작업을 효율적으로 수행하는 방법에 대해 설명합니다.

주의 사항 : 클래스가 직렬화를 재정의하여 새 인스턴스 생성되지 않도록 할 수 있습니다 (예 : 싱글 톤의 경우. 또한 클래스가 Serializable이 아닌 경우 물론 이것은 물론 작동하지 않습니다.

160
Jason Cohen

몇몇 사람들은 Object.clone()을 사용하거나 무시한다고 언급했습니다. 하지 마. Object.clone()에는 몇 가지 중대한 문제가 있으며 대부분의 경우 그 사용이 권장되지 않습니다. 조쉬아 블로흐 (Joshua Bloch)의 " Effective Java "에서 Item 11을 참조하십시오. 난 당신이 안전하게 원시 타입 배열에 Object.clone() 사용할 수 있다고 생각하지만, 당신은 적절하게 사용하고 복제를 무시에 대한 신중해야합니다.

직렬화 (XML 또는 다른 방식)에 의존하는 체계는 어렵습니다.

쉬운 대답은 여기에 없습니다. 오브젝트를 딥 카피하고 싶은 경우는, 오브젝트 그래프를 횡단 해 오브젝트의 카피 생성자 또는 정적 팩토리 메소드를 사용해 명시 적으로 각 아이 오브제를 카피 해, 자식 오브젝트를 깊게 카피합니다. Immutables (예 : Strings)는 복사 할 필요가 없습니다. 제쳐두고, 당신은 이러한 이유로 불변성을 선호해야합니다.

67
Julien Chastang

파일을 만들지 않고도 직렬화로 전체 복사본을 만들 수 있습니다.

딥 복사를 원하는 개체는 implement serializable해야합니다. 클래스가 최종 클래스가 아니거나 수정할 수없는 경우 클래스를 확장하고 직렬화를 구현하십시오.

클래스를 바이트 스트림으로 변환하십시오.

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();

바이트 스트림에서 클래스를 복원하십시오.

ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
(Object) object = (Object) new ObjectInputStream(bais).readObject();
53
Thargor

Apache Commons Lang에서 org.Apache.commons.lang3.SerializationUtils.clone(T) 을 사용하여 직렬화 기반의 딥 클론을 수행 할 수는 있지만 성능은 매우 저조합니다.

일반적으로 복제를 필요로하는 객체 그래프에 객체의 각 클래스에 대해 고유 한 복제 메소드를 작성하는 것이 가장 좋습니다.

36
user8690

전체 복사본을 구현하는 한 가지 방법은 복사 생성자를 각 연결된 클래스에 추가하는 것입니다. 복사 생성자는 'this'의 인스턴스를 단일 인수로 취하여이 인수의 모든 값을 복사합니다. 꽤 많은 일을하지만 꽤 간단하고 안전합니다.

편집 : 필드를 읽는 접근 자 메서드를 사용할 필요가 없습니다. 소스 인스턴스는 항상 복사 생성자가있는 인스턴스와 동일한 유형이므로 모든 필드에 직접 액세스 할 수 있습니다. 분명하지만 간과 할 수도 있습니다.

예:

public class Order {

    private long number;

    public Order() {
    }

    /**
     * Copy constructor
     */
    public Order(Order source) {
        number = source.number;
    }
}


public class Customer {

    private String name;
    private List<Order> orders = new ArrayList<Order>();

    public Customer() {
    }

    /**
     * Copy constructor
     */
    public Customer(Customer source) {
        name = source.name;
        for (Order sourceOrder : source.orders) {
            orders.add(new Order(sourceOrder));
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

편집 : 복사 생성자를 사용하는 경우 복사중인 객체의 런타임 유형을 알아야합니다. 위의 방법을 사용하면 혼합 목록을 쉽게 복사 할 수 없습니다 (일부 리플렉션 코드로 수행 할 수도 있음).

23
Adriaan Koster

Apache commons는 객체를 깊이 복제하는 빠른 방법을 제공합니다.

My_Object object2= org.Apache.commons.lang.SerializationUtils.clone(object1);
18
TheByeByeMan

간단한 API를 가지고 라이브러리 사용 수 있고 리플렉션으로 비교적 빠른 복제를 수행 할 수 있습니다 (직렬화 방법보다 빠름).

Cloner cloner = new Cloner();

MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
17
CorayThan

XStream은 이러한 경우에 유용합니다. 다음은 복제를 수행하는 간단한 코드입니다.

private static final XStream XSTREAM = new XStream();
...

Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
11
sankara

매우 쉽고 간단한 방법 중 하나는 Jackson JSON을 사용하여 복잡한 Java Object를 JSON으로 직렬화하고 다시 읽는 것입니다.

http://wiki.fasterxml.com/JacksonInFiveMinutes

9
Ravi Chinoy

스프링 프레임 워크 사용자. org.springframework.util.SerializationUtils 클래스 사용 :

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
     return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
8
Igor Rybak

XStream ( http://x-stream.github.io/ )을 사용하십시오. 주석을 통해 무시할 수있는 속성을 제어하거나 XStream 클래스의 속성 이름을 명시 적으로 지정할 수도 있습니다. 또한 clonable 인터페이스를 구현할 필요가 없습니다.

8
Adi

깊은 복사는 각 반의 동의하에 만 이루어질 수 있습니다. 클래스 계층을 제어 할 수 있다면 cloneable 인터페이스를 구현하고 Clone 메서드를 구현할 수 있습니다. 그렇지 않으면 개체가 비 데이터 리소스 (예 : 데이터베이스 연결)를 공유 할 수 있기 때문에 전체 복사본을 안전하게 수행하는 것은 안전하지 않습니다. 그러나 일반적으로 딥 복사는 Java 환경에서 나쁜 습관으로 간주되므로 적절한 설계 방법을 통해 피해야합니다.

7
Orion Adrian

복잡한 객체의 경우 성능이 중요하지 않은 경우 json 라이브러리 (예 : gson )를 사용하여 객체를 json 텍스트로 직렬화 한 다음 텍스트를 deserialize하여 새 객체를 얻습니다.

반사를 기반으로하는 gson은 대부분의 경우 작동하지만 transient 필드는 복사되지 않으며 원인이 StackOverflowError 인 순환 참조가있는 개체는 제외됩니다.

public static <T> T copy(T anObject, Class<T> classInfo) {
    Gson gson = new GsonBuilder().create();
    String text = gson.toJson(anObject);
    T newObject = gson.fromJson(text, classInfo);
    return newObject;
}
public static void main(String[] args) {
    String originalObject = "hello";
    String copiedObject = copy(originalObject, String.class);
}
6
tiboo
import com.thoughtworks.xstream.XStream;

public class deepCopy {
    private static  XStream xstream = new XStream();

    //serialize with Xstream them deserialize ...
    public static Object deepCopy(Object obj){
        return xstream.fromXML(xstream.toXML(obj));
    }
}
6
user946968

나는 Dozer Java 객체를 복제하는 데 사용 했으므로 멋지다. Kryo 라이브러리는 또 다른 훌륭한 대안이다.

4
supernova

1)

public static Object deepClone(Object object) {
   try {
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     ObjectOutputStream oos = new ObjectOutputStream(baos);
     oos.writeObject(object);
     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
     ObjectInputStream ois = new ObjectInputStream(bais);
     return ois.readObject();
   }
   catch (Exception e) {
     e.printStackTrace();
     return null;
   }
 }

2)

    // (1) create a MyPerson object named Al
    MyAddress address = new MyAddress("Vishrantwadi ", "Pune", "India");
    MyPerson al = new MyPerson("Al", "Arun", address);

    // (2) make a deep clone of Al
    MyPerson neighbor = (MyPerson)deepClone(al);

여기서 MyPerson 및 MyAddress 클래스는 serilazable 인터페이스를 구현해야합니다.

2
Arun

BeanUtils 콩을 깊이 복제하는 일을 정말 잘합니다.

BeanUtils.cloneBean(obj);
1
Alfergon