148

I have an ArrayList and I want to copy it exactly. I use utility classes when possible on the assumption that someone spent some time making it correct. So naturally, I end up with the Collections class which contains a copy method.

Suppose I have the following:

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

This fails because basically it thinks b isn't big enough to hold a. Yes I know b has size 0, but it should be big enough now shouldn't it? If I have to fill b first, then Collections.copy() becomes a completely useless function in my mind. So, except for programming a copy function (which I'm going to do now) is there a proper way to do this?

4

18 に答える 18

133

b容量は3 ですが、サイズArrayListは 0 です。ある種のバッファ容量があるという事実は実装の詳細です。これはListインターフェイスの一部ではないため、Collections.copy(List, List)使用しません。special-case にするのは醜いでしょうArrayList

tddmonkey が示しているように、コレクションを受け取る ArrayList コンストラクターを使用することが、提供されている例の方法です。

より複雑なシナリオ (実際のコードが含まれる可能性があります) では、Guava内のコレクションが役立つ場合があります。

于 2009-03-27T11:25:22.113 に答える
122

通話中

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、いずれかのメカニズムを介して、基になる型の複雑な知識を持っている必要があります。sの場合String、これは Java (および .NET で言えば) で不変であり、ディープ コピーさえ必要ありません。の場合、MySpecialObjectそのディープ コピーを作成する方法を知る必要がありますが、これは一般的な操作ではありません。


注: 最初に受け入れられた回答は、Collections.copyGoogle でのトップの結果であり、コメントで指摘されているように完全に間違っていました。

于 2009-11-06T03:06:37.020 に答える
60

ただ行う:

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

ArrayList には、別の Collection を受け入れて要素をコピーするコンストラクタがあります

于 2009-03-27T11:25:11.723 に答える
17

Stephen Katulka による回答 (受け入れられた回答) は間違っています (第 2 部)。がディープ コピーを行うことを説明してCollections.copy(b, a);いますが、そうではありません。両方とも、浅いコピーのみを行いますnew ArrayList(a);Collections.copy(b, a);違いは、コンストラクターが新しいメモリを割り当て、割り当てcopy(...)ないことです。これにより、パフォーマンス上の利点があるため、配列を再利用できる場合に適しています。

clone()Java 標準 API はディープ コピーの使用を思いとどまらせようとします。これは、新しいコーダーがこれを定期的に使用するのは良くないためです。これは、デフォルトで公開されていない理由の 1 つでもある可能性があります。

のソース コードは、httpCollections.copy(...) ://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Core/Collections-Jar-Zip-Logging-regex/java/util/ の 552 行にあり ます。 Collections.java.htm

ディープ コピーが必要な場合は、各オブジェクトで for ループと clone() を使用して、アイテムを手動で反復処理する必要があります。

于 2011-02-24T16:38:33.410 に答える
12

List をコピーする最も簡単な方法は、それを新しいリストのコンストラクターに渡すことです。

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

bの浅いコピーになりますa

Collections.copy(List,List)(今まで見たことがない)のソースを見ると、要素のインデックスごとに対処するためのようです。このように要素0を使用List.set(int,E)すると、ターゲットリストなどに要素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'
于 2009-03-27T11:25:42.223 に答える
9
List b = new ArrayList(a.size())

サイズを設定しません。初期容量 (サイズ変更が必要になる前に収まる要素の数) を設定します。この場合のより簡単なコピー方法は次のとおりです。

List b = new ArrayList(a);
于 2009-03-27T11:26:40.147 に答える
8

ホイジュイさんのおっしゃる通り。Stephen Katulka から選択された回答には、Collections.copy に関する誤ったコメントが含まれています。コードの最初の行が彼が望んでいたコピーを行っていたので、おそらく作者はそれを受け入れました。Collections.copy への追加の呼び出しは、再度コピーするだけです。(その結果、コピーが 2 回行われます)。

これを証明するコードは次のとおりです。

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.
        }
    }
}
于 2011-04-07T16:37:41.297 に答える
6

ここでのほとんどの回答は問題を認識していません。ユーザーは最初のリストから2番目のリストへの要素のCOPYを持ちたいと考えています。宛先リストの要素は新しいオブジェクトであり、元のリストの要素への参照ではありません。(2 番目のリストの要素を変更しても、ソース リストの対応する要素の値が変更されないことを意味します。) 可変オブジェクトの場合、元のリスト要素を単純に参照し、コピーしないため、ArrayList(Collection) コンストラクターを使用できません。コピーするときは、オブジェクトごとにリスト クローンが必要です。

于 2011-10-10T09:29:01.150 に答える
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
于 2013-05-01T01:04:31.187 に答える
3

ArrayList をコピーする場合は、次を使用してコピーします。

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

List a = new ArrayList(b);
于 2009-03-27T11:25:59.913 に答える
3
private List<Item> cloneItemList(final List<Item> items)
    {
        Item[] itemArray = new Item[items.size()];
        itemArray = items.toArray(itemArray);
        return Arrays.asList(itemArray);
    }
于 2016-07-27T14:48:41.903 に答える
3

文字列はディープコピーできます

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

それらは不変だからです。他のすべてのオブジェクトではありません --> 自分で反復してコピーする必要があります。

于 2011-04-05T16:42:59.347 に答える
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);               
        }
       */

  }
于 2011-11-28T05:57:03.110 に答える
1

Google guava を使用している場合、1 行のソリューションは次のようになります。

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

これにより、変更可能な配列リスト インスタンスが作成されます。

于 2013-10-11T14:56:25.333 に答える
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())
于 2017-12-14T12:24:42.637 に答える
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]

ただし、ソース コレクションとターゲット コレクションの開始インデックスの追加パラメーターとカウントのパラメーターを受け取るコピー メソッドが必要だと思います。

Java バグ6350752を参照してください。

于 2009-03-27T17:16:14.157 に答える
-1

(sourceList で size() 呼び出しを使用して) 宛先リストのバッキング配列を十分に大きくしたにもかかわらず、Collections.copy() が IndexOutOfBoundsException をスローする理由を理解するには、次の関連する質問の Abhay Yadav による回答を参照してください 。 java.util.List を別の java.util.List にコピーします

于 2016-04-27T18:48:25.517 に答える