web-dev-qa-db-ja.com

Javaコレクションリストをコピーする方法

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

これは、baを保持するのに十分な大きさではないと基本的に考えるため、失敗します。はい、bのサイズが0であることは知っていますが、今では十分に大きいはずです。最初にbを埋めなければならない場合、Collections.copy()は私の頭の中ではまったく役に立たない関数になります。だから、コピー機能をプログラミングすることを除いて(これを行うつもりです)、これを行う適切な方法はありますか?

135
Jasper Floor

呼び出し中

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

a内に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);

また、a内にbの浅いコピーを作成します。最初のパラメーターbに、aのすべての要素を含めるのに十分なcapacity(サイズではない)がない場合、IndexOutOfBoundsExceptionがスローされます。 Collections.copy が機能するために割り当ては必要ないと予想され、もしあれば、その例外をスローします。コピーされたコレクションを事前に割り当てる(b)ことを要求するのは最適化ですが、上記のような奇妙な副作用のないコンストラクターベースの代替手段を考えると、必要なチェックがあるため、この機能は価値があるとは一般的に思いません。

ディープコピーを作成するには、いずれかのメカニズムを介してListが、基になる型の複雑な知識を持っている必要があります。 Java(および.NETで不変)であるStringsの場合、ディープコピーは必要ありません。 MySpecialObjectの場合、その深いコピーを作成する方法を知る必要がありますが、これは一般的な操作ではありません。


注:最初に受け入れられた答えは、GoogleでのCollections.copyの上位結果であり、コメントで指摘されているように間違っていました。

110
Stephen Katulka

bcapacityは3ですが、sizeは0です。 ArrayListが何らかのバッファ容量を持っているという事実は実装の詳細です-Listインターフェイスの一部ではないため、Collections.copy(List, List)は使用しません。特殊な場合ArrayListを使用するのはいでしょう。

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

より複雑なシナリオ(実際のコードが含まれる場合もあります)の場合は、 Google Javaコレクションライブラリ が便利です。

131
Jon Skeet

ただやる:

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

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

58
tddmonkey

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

Java標準APIは、ディープコピーの使用を阻止しようとします。これは、新しいコーダーがこれを定期的に使用すると悪いためです。これは、clone()がデフォルトでパブリックではない理由の1つでもあります。

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()を使用して、アイテムを手動で繰り返す必要があります。

16
hoijui

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

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

baの浅いコピーになります

Collections.copy(List,List)のソースを見ると(これまで見たことがない)、インデックスごとに要素をコピーするためのようです。 List.set(int,E)を使用すると、要素0はターゲットリストなどの要素0を上書きします。認めなければならないjavadocsからは特に明確ではありません。

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'
12
Gareth Davis
List b = new ArrayList(a.size())

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

List b = new ArrayList(a);
9
cletus

Hoijuiが述べているように。 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.
        }
    }
}
8
Michael Welch

なぜ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
5
Vin.X

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

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

文字列は、

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

それらは不変だからです。他のすべてのオブジェクトではなく、自分で繰り返しコピーを行う必要があります。

3
felix

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

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

List a = new ArrayList(b);
3
Martin C.

次の出力は、コピーコンストラクターと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
pwojnowski

他のすべてのオブジェクトではなく、自分で繰り返しコピーを行う必要があります。

これを回避するには、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);               
        }
       */

  }
1
Juan Castillo

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())
1

そして、Google guavaを使用している場合、1行のソリューションは

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

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

0
vsingh

いくつかの値を既存のコレクションにコピーするユースケースを想像するなら、コピーは役に立たないわけではありません。つまり挿入するのではなく、既存の要素を上書きします。

例: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 BUGを参照してください 6350752

0
ordnungswidrig