Java Collections 목록을 복사하는 방법


141

나는 ArrayList 정확하게 복사하고 싶습니다. 누군가가 올바른 시간을 보냈다는 가정하에 가능하면 유틸리티 클래스를 사용합니다. 그래서 자연스럽게, 나는 Collections복사 방법을 포함하는 클래스로 끝납니다 .

내가 다음을 가지고 있다고 가정하십시오.

List<String> a = new ArrayList<String>();
a.add("a");
a.add("b");
a.add("c");
List<String> b = new ArrayList<String>(a.size());

Collections.copy(b,a);

기본적으로 b크기가 충분하지 않다고 생각하기 때문에 실패합니다 a. 예 b, 크기가 0이라는 것을 알고 있지만 이제는 커야합니까? 내가 b먼저 채워야한다면 , Collections.copy()내 마음에 완전히 쓸모없는 기능이됩니다. 복사 기능 프로그래밍 (지금 할 것)을 제외하고는 이것을 수행하는 적절한 방법이 있습니까?


Collections.copy ()의 문서에 "대상 목록은 소스 목록 이상이어야합니다."
DJClayworth

21
나는 대답이 맞다고 생각하지 않는다
Bozho

3
Jasper Floor라는 잘못된 답변을 수락했습니다. 코드에 잘못된 정보를 사용하지 않았기를 진심으로 바랍니다!
Malcolm

답변:


115

부름

List<String> b = new ArrayList<String>(a);

awithin 의 얕은 사본을 만듭니다 b. 모든 요소는 b순서와 동일한 순서대로 존재합니다 a.

마찬가지로 전화

// note: instantiating with a.size() gives `b` enough capacity to hold everything
List<String> b = new ArrayList<String>(a.size());
Collections.copy(b, a);

awithin 의 얕은 사본을 만듭니다 b. 첫 번째 매개 변수에 모든 요소 를 포함 b 있는 충분한 용량 (크기가 아님) a이없는 경우을 발생 IndexOutOfBoundsException시킵니다. 예상 Collections.copy대로 작동하는 데 할당이 필요하지 않으며 ,있는 경우 해당 예외가 발생합니다. 복사 된 컬렉션을 미리 할당해야하는 최적화입니다 (b ). 일반적으로 위의 그림과 같이 이상한 부작용이없는 생성자 기반 대안이 주어지면 필요한 검사로 인해 기능이 가치가 있다고 생각하지 않습니다.

딥 카피를 만들려면 List두 메커니즘 중 하나를 통해 기본 유형에 대한 복잡한 지식이 있어야합니다. StringJava (및 해당 문제의 경우 .NET)에서 변경할 수없는 s 의 경우 딥 카피조차 필요하지 않습니다. 의 경우 MySpecialObject, 사본을 만드는 방법을 알아야하며 이는 일반적인 작업이 아닙니다.


참고 : 원래 허용 된 답변은 Collections.copyGoogle에서 최고 결과 였으며 의견에서 지적한대로 잘못 표시되었습니다.


1
@ncasas 그렇습니다. Java에는 일반적인 "복사"기능이 없다는 사실에 대해 슬퍼하고 있습니다. 실제로 다른 저자들이 자신의 클래스에 대해 clone ()을 구현하지 않은 경우가 종종 있습니다. 어떤 종류의 객체 복사도 할 수없는 상태로 남겨 둡니다. 또는 더 나쁜 것은 문서가 없거나 불량한 구현 된 복제 방법을 사용하여 복제 기능을 사용할 수 없게 만드는 것입니다 (신뢰할 수 있고 실제적인 "알고있는 일"을 이해하는 의미에서).
Malcolm

133

b보유 용량이 3이지만 크기 0 사실 ArrayList는 부분 아니다 - 버퍼 용량 일종의 구현 상세 가지고 List있으므로 인터페이스 Collections.copy(List, List)를 사용하지 않고있다. 특별한 경우에는 추한 것 ArrayList입니다.

MrWiggles가 지적했듯이, 컬렉션을 취하는 ArrayList 생성자를 사용하는 것이 제공된 예제의 방법입니다.

더 복잡한 시나리오 (실제 코드를 포함 할 수 있음)의 경우 구아바 내의 모음이 유용 할 수 있습니다 .


58

그냥 해:

List a = new ArrayList(); 
a.add("a"); 
a.add("b"); 
a.add("c"); 
List b = new ArrayList(a);

ArrayList에는 다른 Collection을 받아 요소를 복사하는 생성자가 있습니다.


7
아래의 의견에 따르면, 이것은 얕은 사본입니다. 그렇지 않으면 이것은 정답 일 것입니다. 나는 그것을 지정해야한다고 가정합니다. 어쨌든 나는 움직였다.
재스퍼 플로어

11
문자열 목록의 경우 String객체를 변경할 수 없으므로 딥 카피는 중요하지 않습니다 .
Derek Mahar

17

Stephen Katulka의 답변 (허용 된 답변)이 잘못되었습니다 (두 번째 부분). 그것은 Collections.copy(b, a);딥 카피 를 수행 한다고 설명 하지만 그렇지 않습니다. 둘, new ArrayList(a);그리고 Collections.copy(b, a);단지 얕은 사본을한다. 차이점은 생성자가 새 메모리를 할당 copy(...)하지만 그렇지 않은 경우 성능 이점이 있으므로 배열을 재사용 할 수있는 경우에 적합하다는 것입니다.

Java 표준 API는 딥 카피 사용을 권장하지 않습니다. 새로운 코더가 정기적으로 이것을 사용하면 좋지 않을 수 있습니다 clone(). 이는 기본적으로 공개되지 않는 이유 중 하나 일 수도 있습니다 .

소스 코드는 Collections.copy(...)552 행에서 확인할 수 있습니다. http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Core/Collections-Jar-Zip-Logging-regex/java/util/ Collections.java.htm

딥 카피가 필요한 경우 각 객체에서 for 루프와 clone ()을 사용하여 항목을 수동으로 반복해야합니다.


12

List를 복사하는 가장 간단한 방법은 새 목록의 생성자에게 전달하는 것입니다.

List<String> b = new ArrayList<>(a);

b 얕은 사본이 될 것입니다 a

Collections.copy(List,List)(나는 전에 본 적이 없다) 의 출처를 보면 인덱스로 요소 색인을 처리하는 것으로 보입니다. List.set(int,E)따라서 요소 0을 사용 하면 대상 목록 등의 요소 0을 덮어 씁니다. 인정해야 할 javadoc에서 특별히 명확하지 않습니다.

List<String> a = new ArrayList<>(a);
a.add("foo");
b.add("bar");

List<String> b = new ArrayList<>(a); // shallow copy 'a'

// the following will all hold
assert a.get(0) == b.get(0);
assert a.get(1) == b.get(1);
assert a.equals(b);
assert a != b; // 'a' is not the same object as 'b'

왜 '얕은'사본을 말합니까? -나 자바 멍청한
Martlark

4
'얕은 복사'는 복사 후 b의 객체가 사본이 아닌 a와 동일한 객체임을 의미합니다.
DJClayworth

1
Collections.copy ()에 대한 javadoc은 "대상 목록은 최소한 소스 목록만큼 길어야합니다."라고 말합니다.
DJClayworth

나는 그 기능이 실제로 무엇을하는지보기 위해 몇 번의 외모를 필요로했고, 질문자가 그 기능과 정확히 혼동 된 것을 볼 수있다.
Gareth Davis

나는 그것이 확실하지 않다? 문자열은 변경할 수 없으므로 참조 만 동일하지 않습니다. 그러나, u는 두 목록의 요소를 돌연변이하려고해도, 다른 목록에 결코 돌연변이 같은 요소
데이비드 T.

9
List b = new ArrayList(a.size())

크기를 설정하지 않습니다. 초기 용량을 설정합니다 (크기를 조정하기 전에 몇 개의 요소를 넣을 수 있는지). 이 경우 더 간단한 복사 방법은 다음과 같습니다.

List b = new ArrayList(a);

8

Hoijui가 언급했듯이. Stephen Katulka가 선택한 답변에 Collections.copy에 대한 의견이 잘못되었습니다. 저자는 아마도 첫 번째 코드 행이 원하는 사본을 작성했기 때문에 승인했을 것입니다. Collections.copy에 대한 추가 호출은 다시 복사됩니다. (복사 결과 두 번 발생 함).

그것을 증명하는 코드는 다음과 같습니다.

public static void main(String[] args) {

    List<String> a = new ArrayList<String>();
    a.add("a");
    a.add("b");
    a.add("c");
    List<String> b = new ArrayList<String>(a);

    System.out.println("There should be no output after this line.");

    // Note, b is already a shallow copy of a;
    for (int i = 0; i < a.size(); i++) {
        if (a.get(i) != b.get(i)) {
            System.out.println("Oops, this was a deep copy."); // Note this is never called.
        }
    }

    // Now use Collections.copy and note that b is still just a shallow copy of a
    Collections.copy(b, a);
    for (int i = 0; i < a.size(); i++) {
        if (a.get(i) != b.get(i)) {
            System.out.println("Oops, i was wrong this was a deep copy"); // Note this is never called.
        }
    }

    // Now do a deep copy - requires you to explicitly copy each element
    for (int i = 0; i < a.size(); i++) {
        b.set(i, new String(a.get(i)));
    }

    // Now see that the elements are different in each 
    for (int i = 0; i < a.size(); i++) {
        if (a.get(i) == b.get(i)) {
            System.out.println("oops, i was wrong, a shallow copy was done."); // note this is never called.
        }
    }
}

5

여기에있는 대부분의 대답은 문제를 깨닫지 못합니다. 사용자는 첫 번째 목록에서 두 번째 목록으로 요소의 사본을 원합니다. 대상 목록 요소는 새로운 객체이며 원래 목록의 요소를 참조하지 않습니다. (두 번째 목록의 요소를 변경한다는 것은 소스 목록의 해당 요소에 대한 값을 변경해서는 안됨을 의미합니다.) 가변 객체의 경우 원래 목록 요소를 간단하게 참조하고 복사하지 않기 때문에 ArrayList (Collection) 생성자를 사용할 수 없습니다. 복사 할 때 각 객체에 대해 목록 복제기가 있어야합니다.


5

addAll메소드를 사용하지 않습니까?

    List a = new ArrayList();
         a.add("1");
         a.add("abc");

    List b = b.addAll(listA);

//b will be 1, abc

b에 기존 항목이 있거나 다음과 같은 일부 요소를 보류하려는 경우에도 :

List a = new ArrayList();
     a.add("1");
     a.add("abc");

List b = new ArrayList();
     b.add("x");
     b.addAll(listA);
     b.add("Y");

//b will be x, 1, abc, Y

3

ArrayList를 복사하려면 다음을 사용하여 복사하십시오.

List b = new ArrayList();
b.add("aa");
b.add("bb");

List a = new ArrayList(b);

3

문자열을 사용하여 딥 카피 가능

List<String> b = new ArrayList<String>(a);

그들은 불변이기 때문에. 다른 모든 객체는-> 직접 반복하고 복사해야합니다.


8
배열의 각 요소 b가의 동일한 해당 String객체를 가리 키기 때문에 이것은 여전히 ​​얕 습니다 a. 그러나 지적한 바와 같이 String객체는 변경할 수 없기 때문에 이것은 중요하지 않습니다 .
Derek Mahar

3
private List<Item> cloneItemList(final List<Item> items)
    {
        Item[] itemArray = new Item[items.size()];
        itemArray = items.toArray(itemArray);
        return Arrays.asList(itemArray);
    }

4
답변에 약간의 설명을 추가해주세요
Sampada

1
이 코드는 질문에 대답 할 수 있지만 문제를 해결하는 방법 및 / 또는 이유 에 대한 추가 컨텍스트를 제공 하면 답변의 장기적인 가치가 향상됩니다.
마이클 파커

1

다른 모든 객체는-> 직접 반복하고 복사해야합니다.

이를 피하려면 Cloneable을 구현하십시오.

public class User implements Serializable, Cloneable {

    private static final long serialVersionUID = 1L;

    private String user;
    private String password;
    ...

    @Override
    public Object clone() {
        Object o = null;
        try {
          o = super.clone();
        } catch(CloneNotSupportedException e) {
        }
        return o;
     }
 }

....

  public static void main(String[] args) {

      List<User> userList1 = new ArrayList<User>();

      User user1 = new User();
      user1.setUser("User1");
      user1.setPassword("pass1");
      ...

      User user2 = new User();
      user2.setUser("User2");
      user2.setPassword("pass2");
      ...

      userList1 .add(user1);
      userList1 .add(user2);

      List<User> userList2 = new ArrayList<User>();


      for(User u: userList1){
          u.add((User)u.clone());
      }

      //With this you can avoid 
      /*
        for(User u: userList1){
            User tmp = new User();
            tmp.setUser(u.getUser);
            tmp.setPassword(u.getPassword);
            ...
            u.add(tmp);               
        }
       */

  }

2
"userList2.add ((User) u.clone ());"이 아니어야합니다. ?
KrishPrabakar

1

다음 출력은 복사 생성자와 Collections.copy () 사용 결과를 보여줍니다.

Copy [1, 2, 3] to [1, 2, 3] using copy constructor.

Copy [1, 2, 3] to (smaller) [4, 5]
java.lang.IndexOutOfBoundsException: Source does not fit in dest
        at java.util.Collections.copy(Collections.java:556)
        at com.farenda.java.CollectionsCopy.copySourceToSmallerDest(CollectionsCopy.java:36)
        at com.farenda.java.CollectionsCopy.main(CollectionsCopy.java:14)

Copy [1, 2] to (same size) [3, 4]
source: [1, 2]
destination: [1, 2]

Copy [1, 2] to (bigger) [3, 4, 5]
source: [1, 2]
destination: [1, 2, 5]

Copy [1, 2] to (unmodifiable) [4, 5]
java.lang.UnsupportedOperationException
        at java.util.Collections$UnmodifiableList.set(Collections.java:1311)
        at java.util.Collections.copy(Collections.java:561)
        at com.farenda.java.CollectionsCopy.copyToUnmodifiableDest(CollectionsCopy.java:68)
        at com.farenda.java.CollectionsCopy.main(CollectionsCopy.java:20)

전체 프로그램의 소스는 다음과 같습니다. Java List copy . 그러나 출력은 java.util.Collections.copy ()의 작동 방식을보기에 충분합니다.


1

Google 구아바를 사용하는 경우 한 줄 해결책은

List<String> b = Lists.newArrayList(a);

가변 배열 목록 인스턴스를 만듭니다.


1

Java 8이 null 안전하므로 다음 코드를 사용할 수 있습니다.

List<String> b = Optional.ofNullable(a)
                         .map(list -> (List<String>) new ArrayList<>(list))
                         .orElseGet(Collections::emptyList);

또는 수집기를 사용하여

List<String> b = Optional.ofNullable(a)
                         .map(List::stream)
                         .orElseGet(Stream::empty)
                         .collect(Collectors.toList())

0

유스 케이스에서 일부 값을 기존 콜렉션에 복사한다고 가정하면 복사는 쓸모가 없습니다. 즉, 삽입하는 대신 기존 요소를 덮어 쓰려고합니다.

예 : a = [1,2,3,4,5] b = [2,2,2,2,3,3,3,3,3,4,4,4,] a.copy (b) = [1,2,3,4,5,3,3,3,3,4,4,4]

그러나 소스 및 대상 컬렉션의 시작 색인에 대한 추가 매개 변수와 count 매개 변수를 취하는 복사 방법이 필요합니다.

Java BUG 6350752 참조


-1

Collections.copy은 () IndexOutOfBoundsException가를 던졌습니다 왜합니다 (SOURCELIST의 크기 () 호출을 통해) 대상 목록 충분히 큰의 보조 배열을 만든 적이 있지만 이해하기 위해이 관련 문제의 Abhay 야다 브에 의해 대답을 참조하십시오 방법 java.util.List를 다른 java.util.List에 복사

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.