ArrayList를 복제하고 내용을 복제하는 방법은 무엇입니까?


273

ArrayListJava에서 항목을 복제하고 항목을 복제하는 방법은 무엇입니까?

예를 들어

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = ....something to do with dogs....

그리고 나는 안에 clonedList있는 객체 가 개 목록과 같지 않을 것으로 기대 합니다.


그것은 딥 클론 유틸리티 추천 질문 에서 이미 논의되었습니다
Swiety

답변:


199

항목을 반복하고 하나씩 복제하여 복제를 결과 배열에 배치해야합니다.

public static List<Dog> cloneList(List<Dog> list) {
    List<Dog> clone = new ArrayList<Dog>(list.size());
    for (Dog item : list) clone.add(item.clone());
    return clone;
}

이를 위해서는 Dog클래스가 Cloneable인터페이스 를 구현 하고 clone()메서드를 재정의하도록해야 합니다.


19
그러나 일반적으로 할 수는 없습니다. clone ()은 Cloneable 인터페이스의 일부가 아닙니다.
Michael Myers

13
그러나 clone ()은 Object에서 보호되므로 액세스 할 수 없습니다. 해당 코드를 컴파일하십시오.
Michael Myers

5
모든 클래스는 Object를 확장하므로 clone ()을 재정의 할 수 있습니다. 이것이 Cloneable의 목적입니다!
Stephan202

2
이것은 좋은 대답입니다. Cloneable은 실제로 인터페이스입니다. 그러나 mmyers는 clone () 메소드가 Object 클래스에 선언 된 보호 된 메소드라는 점에서 중요한 점이 있습니다. Dog 클래스에서이 메소드를 대체하고 필드를 수동으로 복사해야합니다.
Jose

3
필자는 팩토리 또는 빌더 또는 정적 메소드를 작성하여 Dog의 인스턴스를 가져 와서 수동으로 필드를 새 인스턴스에 복사하고 새 인스턴스를 리턴한다고 말합니다.
Jose

196

개인적으로 Dog에 생성자를 추가합니다.

class Dog
{
    public Dog()
    { ... } // Regular constructor

    public Dog(Dog dog) {
        // Copy all the fields of Dog.
    }
}

그런 다음 Varkhan의 답변에 표시된 것처럼 반복하십시오.

public static List<Dog> cloneList(List<Dog> dogList) {
    List<Dog> clonedList = new ArrayList<Dog>(dogList.size());
    for (Dog dog : dogList) {
        clonedList.add(new Dog(dog));
    }
    return clonedList;
}

이것의 장점은 Java에서 깨진 Cloneable 항목으로 해결할 필요가 없다는 것입니다. 또한 Java 콜렉션을 복사하는 방법과 일치합니다.

또 다른 옵션은 ICloneable 인터페이스를 직접 작성하여 사용하는 것입니다. 그렇게하면 복제를위한 일반적인 방법을 작성할 수 있습니다.


DOG의 모든 필드를 복사하여 더 구체적으로 지정할 수 있습니다. 나는 이해하지 못한다 :(
Dr. aNdRO

정의되지 않은 객체 (개 대신)에 대해 해당 함수를 작성할 수 있습니까?
Tobi G.

@TobiG. 무슨 말인지 모르겠어요 당신은시겠습니까 cloneList(List<Object>)Dog(Object)?
cdmckay

@cdmckay cloneList (List <Object>), cloneList (List <Dog>) 및 cloneList (List <Cat>)에서 작동하는 하나의 함수입니다. 그러나 당신은 내가 생각하는 일반적인 생성자를 호출 할 수 없습니다 ...?
Tobi G.

@TobiG. 그렇다면 일반적인 복제 기능처럼? 그것은 실제로이 질문에 관한 것이 아닙니다.
cdmckay

143

모든 표준 컬렉션에는 복사 생성자가 있습니다. 그것을 써.

List<Double> original = // some list
List<Double> copy = new ArrayList<Double>(original); //This does a shallow copy

clone()여러 가지 실수로 설계되었으므로 ( 이 질문 참조 ) 피하는 것이 가장 좋습니다.

에서 효과적인 자바 2 판 , 항목 11 : 재정의 클론 신중하게

Cloneable과 관련된 모든 문제가 주어지면 다른 인터페이스가 확장해서는 안되며 상속을 위해 설계된 클래스 (항목 17)는 구현해서는 안된다고 말하는 것이 안전합니다. 많은 단점이 있기 때문에 일부 전문가 프로그래머는 단순히 복제 방법을 재정의하지 않고 배열을 복사하는 것 외에는 호출하지 않기로 결정합니다. 상속을 위해 클래스를 디자인 할 경우, 잘 동작하는 보호 된 클론 메소드를 제공하지 않기로 선택하면 서브 클래스가 Cloneable을 구현할 수 없습니다.

이 책은 또한 복사 생성자가 복제 가능 / 복제에 비해 많은 장점을 설명합니다.

  • 위험이 발생하기 쉬운 외 언어 적 객체 생성 메커니즘에 의존하지 않습니다.
  • 그들은 얇게 문서화 된 관습에 대해 강제 할 수없는 준수를 요구하지 않습니다
  • 최종 필드의 올바른 사용과 충돌하지 않습니다
  • 그들은 불필요한 점검 예외를 던지지 않습니다.
  • 그들은 캐스트가 필요하지 않습니다.

복사 생성자를 사용하는 또 다른 이점을 고려 : 당신은이 가정 HashSet s, 당신은로 복사 할 TreeSet. clone 메소드는이 기능을 제공 할 수 없지만 다음과 같이 변환 생성자를 사용하면 쉽습니다 new TreeSet(s).


85
내가 아는 한 표준 컬렉션의 복사 생성자 는 깊은 복사본이 아닌 얕은 복사본을 만듭니다 . 여기에서 묻는 질문은 깊은 복사 답변을 찾습니다.
Abdull

20
이것은 단순히 잘못, 복사 구성자는 얕은 복사를 수행-질문의
전제

1
이 답변의 올바른 점은 목록의 객체를 변경하지 않는 경우 항목을 추가하거나 제거해도 두 목록에서 모두 제거되지 않는다는 것입니다. 간단한 과제 만큼 얕지는 않습니다 .
누 메논

42

: 자바 8 우아하고 컴팩트 요소 개에 복사 생성자 또는 복제 메서드를 호출 할 수있는 새로운 방법을 제공합니다 스트림 , 람다수집을 .

복사 생성자 :

List<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toList());

표현식을 메소드 참조Dog::new 라고합니다 . 다른 개를 인수로 취하는 생성자를 호출하는 함수 객체를 만듭니다 .Dog

복제 방법 [1] :

List<Dog> clonedDogs = dogs.stream().map(d -> d.clone()).collect(toList());

ArrayList결과로 얻기

또는 ArrayList다시 연락 을 받아야하는 경우 (나중에 수정하려는 경우) :

ArrayList<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toCollection(ArrayList::new));

목록을 제자리에 업데이트

dogs목록 의 원래 내용을 유지할 필요가없는 경우 대신 replaceAll방법을 사용하여 목록을 업데이트하십시오.

dogs.replaceAll(Dog::new);

모든 예제는 가정 import static java.util.stream.Collectors.*;합니다.


에 대한 수집가 ArrayList

마지막 예제의 콜렉터는 util 메소드로 만들 수 있습니다. 이것은 일반적인 일이기 때문에 나는 개인적으로 짧고 예쁘기를 좋아합니다. 이처럼 :

ArrayList<Dog> clonedDogs = dogs.stream().map(d -> d.clone()).collect(toArrayList());

public static <T> Collector<T, ?, ArrayList<T>> toArrayList() {
    return Collectors.toCollection(ArrayList::new);
}

[1] 참고 사항 CloneNotSupportedException:

이 솔루션이 작동하려면의 clone메소드 가이를 선언한다고 선언 Dog 해서는 안됩니다CloneNotSupportedException . 그 이유는 인수가 map점검 된 예외를 처리 할 수 ​​없기 때문입니다 .

이처럼 :

    // Note: Method is public and returns Dog, not Object
    @Override
    public Dog clone() /* Note: No throws clause here */ { ...

그러나 이것이 가장 좋은 방법이므로 큰 문제는 아닙니다. ( 예를 들어 Effect Java 는이 조언을 제공합니다.)

이 사실을 알려 주신 Gustavo에게 감사드립니다.


추신:

더 예쁘다면 대신 메소드 참조 구문을 사용하여 똑같은 일을 할 수 있습니다.

List<Dog> clonedDogs = dogs.stream().map(Dog::clone).collect(toList());

Dog (d)가 복사 생성자 인 경우 이러한 방식으로 수행하면 성능에 영향을 미치나요? List<Dog> clonedDogs = new ArrayList<>(); dogs.stream().parallel().forEach(d -> clonedDogs.add(new Dog(d)));
SaurabhJinturkar

1
@SaurabhJinturkar : 버전이 스레드 안전하지 않으므로 병렬 스트림과 함께 사용해서는 안됩니다. parallel호출이 clonedDogs.add여러 스레드에서 동시에 호출 되기 때문 입니다. 사용하는 버전 collect은 스레드로부터 안전합니다. 이것은 스트림 라이브러리의 기능 모델의 장점 중 하나이며, 동일한 코드를 병렬 스트림에 사용할 수 있습니다.
Lii

1
@SaurabhJinturkar : 또한 수집 작업이 빠릅니다. 버전과 거의 동일하지만 병렬 스트림에서도 작동합니다. 예를 들어 배열 목록 대신 동시 대기열을 사용하여 버전을 수정할 수는 있지만 훨씬 느릴 것이라고 확신합니다.
Lii

난 당신의 솔루션을 사용하려고 할 때마다 나는 얻을 Unhandled exception type CloneNotSupportedExceptiond.clone(). 예외를 선언하거나 잡으면 해결되지 않습니다.
구스타보

@ 구스타보 (Gustavo) : 복제중인 객체 ( Dog이 예에서는)가 복제를 지원하지 않기 때문에 거의 확실합니다 . Clonable인터페이스를 구현 하시겠습니까?
Lii

28

기본적으로 수동 반복없이 세 가지 방법이 있습니다.

1 생성자 사용

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>(dogs);

2 사용 addAll(Collection<? extends E> c)

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>();
clonedList.addAll(dogs);

3 파라미터 addAll(int index, Collection<? extends E> c)와 함께 방법 사용int

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>();
clonedList.addAll(0, dogs);

NB : 작업이 진행되는 동안 지정된 컬렉션이 수정되면 이러한 작업의 동작이 정의되지 않습니다.


50
이 3 가지 변형 모두 목록의 얕은 사본 만 생성한다는 점을 명심하십시오.
electrobabe

9
이것은 딥 클론이 아니며, 그 두 목록은 동일한 객체를 유지하고 단지 참조 만 복사했지만 Dog 객체는 하나의 목록을 수정하면 다음 목록이 동일하게 변경됩니다. 너무 많은 공감대.
Saorikido

1
@ Neeson.Z 모든 메소드는 목록의 깊은 사본과 목록 요소의 얕은 사본을 작성합니다. 목록의 요소를 수정하면 변경 사항이 다른 목록에 반영되지만 목록 중 하나를 수정하면 (예 : 개체 제거) 다른 목록은 변경되지 않습니다.
Alessandro Teruzzi 2016 년

17

나는 현재 녹색 답변이 나쁘다고 생각합니다 . 왜 물어 볼까요?

  • 많은 코드를 추가해야 할 수 있습니다
  • 복사 할 모든 목록을 나열 하고이 작업을 수행해야합니다

직렬화도 좋지 않은 방법으로, 모든 곳에서 직렬화 가능을 추가해야 할 수도 있습니다.

그래서 해결책은 무엇입니까?

자바 라이브러리를 깊은 복제 클로닝 라이브러리는 작은 오픈 소스 (아파치 라이선스) 자바 라이브러리 깊은 클론 객체입니다. 객체는 Cloneable 인터페이스를 구현할 필요가 없습니다. 사실상이 라이브러리는 모든 Java 객체를 복제 할 수 있습니다. 캐시 된 개체를 수정하지 않으려는 경우 또는 개체의 깊은 복사본을 만들 때마다 캐시 구현에서 사용할 수 있습니다.

Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);

https://github.com/kostaskougios/cloning 에서 확인 하십시오


8
이 방법의 한 가지주의 할 점은 반사를 사용한다는 것입니다. 반사는 Varkhan의 솔루션보다 약간 느릴 수 있습니다.
cdmckay

6
나는 "많은 코드가 필요하다"는 첫 번째 요점을 이해하지 못한다. 당신이 이야기하는 라이브러리에는 더 많은 코드가 필요합니다. 어디에 배치 하느냐의 문제입니다. 그렇지 않으면 나는 이런 종류의 일에 도움이되는 특별한 도서관에 동의합니다.
nawfal

8

json을 사용하여 목록을 직렬화 / 직렬화 해제 할 수있는 방법을 찾았습니다. 직렬화리스트는 직렬화 해제시 원래 오브젝트에 대한 참조를 보유하지 않습니다.

gson 사용하기 :

List<CategoryModel> originalList = new ArrayList<>(); // add some items later
String listAsJson = gson.toJson(originalList);
List<CategoryModel> newList = new Gson().fromJson(listAsJson, new TypeToken<List<CategoryModel>>() {}.getType());

jackson과 다른 json 라이브러리를 사용하여 그렇게 할 수 있습니다.


1
사람들이 왜이 답변을 다운 보트했는지 모르겠습니다. 다른 답변은 clone ()을 구현하거나 새 라이브러리를 포함하도록 종속성을 변경해야합니다. 그러나 JSon 라이브러리의 대부분의 프로젝트는 이미 포함되었을 것입니다. 나는 이것을 찬성했다.
Satish

1
@Satish 예, 이것은 나를 도운 유일한 대답입니다. 다른 사람들에게 무엇이 잘못되었는지 잘 모르겠지만 복제본을 사용하거나 복제 생성자를 사용하면 원본 목록이 업데이트되었지만 원래는 업데이트되지 않았습니다. 저자 덕분에!
Parag Pawar

지식을위한 순수한 Java 응답은 아니지만이 문제를 신속하게 처리 할 수있는 효율적인 솔루션 인 것이 사실입니다.
marcRDZ

해킹 큰, 시간을 할애 저장
MXML

6

나는 항상이 옵션을 사용했다 :

ArrayList<Dog> clonedList = new ArrayList<Dog>(name_of_arraylist_that_you_need_to_Clone);

2

ArrayList수동 으로 복제해야합니다 (반복하고 각 요소를 새 요소에 복사하여 ArrayList) clone(). 그 이유는에 포함 된 객체가 자체적으로 ArrayList구현되지 않았기 Clonable때문입니다.

편집 : ... 그것이 바로 Varkhan의 코드가하는 일입니다.


1
그리고 그렇게하더라도 리플렉션 이외의 clone ()에 액세스 할 수있는 방법이 없으며 어쨌든 성공을 보장하지 않습니다.
Michael Myers

1

불쾌한 방법은 반사와 함께하는 것입니다. 이와 같은 것이 나를 위해 일했습니다.

public static <T extends Cloneable> List<T> deepCloneList(List<T> original) {
    if (original == null || original.size() < 1) {
        return new ArrayList<>();
    }

    try {
        int originalSize = original.size();
        Method cloneMethod = original.get(0).getClass().getDeclaredMethod("clone");
        List<T> clonedList = new ArrayList<>();

        // noinspection ForLoopReplaceableByForEach
        for (int i = 0; i < originalSize; i++) {
            // noinspection unchecked
            clonedList.add((T) cloneMethod.invoke(original.get(i)));
        }
        return clonedList;
    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
        System.err.println("Couldn't clone list due to " + e.getMessage());
        return new ArrayList<>();
    }
}

깔끔하고 불쾌한 트릭! 하나의 잠재적 인 문제 : original다른 클래스의 객체가 포함되어 있으면 cloneMethod.invoke잘못된 종류의 객체로 호출하면 예외로 실패 한다고 생각 합니다. 이 때문에 Method각 객체에 대해 특정 클론을 검색하는 것이 좋습니다 . 또는 clone 메소드를 사용하십시오 Object(그러나 그 방법 은 보호되어 더 많은 경우 실패 할 수 있습니다).
Lii

또한 빈 목록을 반환하는 대신 catch-clause에서 런타임 예외를 throw하는 것이 좋습니다.
Lii

1
List<Dog> dogs;
List<Dog> copiedDogs = dogs.stream().map(dog -> SerializationUtils.clone(dog)).Collectors.toList());

이것은 각 개를 깊게 복사합니다


0

다른 포스터는 정확합니다. 목록을 반복하고 새 목록으로 복사해야합니다.

그러나 ... 목록의 객체가 변경 불가능한 경우 복제 할 필요가 없습니다. 객체에 복잡한 객체 그래프가 있으면 변경할 수 없어야합니다.

불변성의 또 다른 이점은 스레드 안전성도 있다는 것입니다.


0

다음은 일반적인 템플릿 유형을 사용하는 솔루션입니다.

public static <T> List<T> copyList(List<T> source) {
    List<T> dest = new ArrayList<T>();
    for (T item : source) { dest.add(item); }
    return dest;
}

제네릭은 좋지만 질문에 대답하려면 항목을 복제해야합니다.
David Snabel-Caunt를

0

당신을 위해 객체는 clone () 메소드를 재정의합니다.

class You_class {

    int a;

    @Override
    public You_class clone() {
        You_class you_class = new You_class();
        you_class.a = this.a;
        return you_class;
    }
}

Vector obj 또는 ArraiList obj ...에 대해 .clone ()을 호출하십시오.


0

javas 라이브러리를 commons-lang-2.3.jar를 사용하여 목록을 복제하여 쉬운 방법

링크 다운로드 commons-lang-2.3.jar

사용하는 방법

oldList.........
List<YourObject> newList = new ArrayList<YourObject>();
foreach(YourObject obj : oldList){
   newList.add((YourObject)SerializationUtils.clone(obj));
}

나는 이것이 도움이되기를 바랍니다.

:디


1
참고로 왜 Commons Lang의 오래된 버전입니까? 여기에서 릴리스 내역을 확인하십시오 : commons.apache.org/proper/commons-lang/release-history.html
informatik01

0

패키지 import org.apache.commons.lang.SerializationUtils;

방법이 있습니다 SerializationUtils.clone(Object);

this.myObjectCloned = SerializationUtils.clone(this.object);

이 질문에 대답하기에는 약간 구식입니다. 그리고 질문 아래의 의견에 많은 다른 답변이 있습니다.
moskito-x


0

아래는 나를 위해 일했다 ..

Dog.java에서

public Class Dog{

private String a,b;

public Dog(){} //no args constructor

public Dog(Dog d){ // copy constructor
   this.a=d.a;
   this.b=d.b;
}

}

 -------------------------

 private List<Dog> createCopy(List<Dog> dogs) {
 List<Dog> newDogsList= new ArrayList<>();
 if (CollectionUtils.isNotEmpty(dogs)) {
 dogs.stream().forEach(dog-> newDogsList.add((Dog) SerializationUtils.clone(dog)));
 }
 return newDogsList;
 }

createCopy 메소드에서 작성된 새 목록은 SerializationUtils.clone ()을 통해 작성됩니다. 따라서 새 목록을 변경해도 원래 목록에는 영향을 미치지 않습니다.


-1

딥 카피 ArrayList를 만드는 가장 쉬운 방법을 찾았습니다. String ArrayList arrayA를 복사한다고 가정합니다.

ArrayList<String>arrayB = new ArrayList<String>();
arrayB.addAll(arrayA);

그것이 효과가 없다면 알려주세요.


3
예를 들어 List <List <JsonObject >>를 사용하면 작동하지 않습니다
djdance

문자열은 변경할 수 없습니다. 복제는 의미가 없으며 예제에서 arrayB와 arrayA는 동일한 객체 참조-얕은 사본입니다.
Christian Fries
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.