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

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

예를 들어

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

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



답변

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

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()메서드를 재정의하도록해야 합니다.


답변

개인적으로 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 인터페이스를 직접 작성하여 사용하는 것입니다. 그렇게하면 복제를위한 일반적인 방법을 작성할 수 있습니다.


답변

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

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).


답변

: 자바 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());


답변

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

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 : 작업이 진행되는 동안 지정된 컬렉션이 수정되면 이러한 작업의 동작이 정의되지 않습니다.


답변

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

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

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

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

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

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

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


답변

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 라이브러리를 사용하여 그렇게 할 수 있습니다.