Javaで汎用リストを複製するにはどうすればよいですか?


155

私が持っているArrayList<String>、私はのコピーを返すのが好きだろうと。 ArrayList次のシグネチャを持つクローンメソッドがあります。

public Object clone()

このメソッドを呼び出した後、返されたオブジェクトをどのようにキャストしArrayList<String>ますか?


16
いいえ、これは有効な質問です。Javaは「真の」総称をサポートしておらず、ランタイム型の消去などもすべてサポートしているため、この種の詳細は扱いにくい場合があります。さらに、CloneableインターフェースとObject.clone()メソッドのメカニズムも同様に混乱します。
アウトロープログラマ

OK、私はたいていC#をやっていて、これは本当に簡単です。この質問からコメントを削除する場合はお知らせください。
Espo

1
コメントを残すことができます。私の編集は私が問題を抱えていたものを説明したと思います。
リザードに請求する

2
あなたのコメントは、少し見下がっていればOKです。Java開発者がジャンプしなければならない多くのフープが.NET開発者にはばかげているように思えます。
アウトロープログラマ

1
@オスカー、彼はクローンを作成し、クローンコマンドを呼び出さないことを望んでいます。クローンが実際にクローンを作成するわけではありません。これがポイントだと思います。これは確かにトリッキーな質問です。
Rafa

回答:


62
ArrayList newArrayList = (ArrayList) oldArrayList.clone();

43
これは文字列では問題なく機能しますが(これは質問が要求したものです)、ArrayList.cloneが浅いコピーを実行するため、リストに変更可能なオブジェクトがあった場合、それらは複製されません(そして1つを変更します)一方のリストでは、もう一方のリストでも変更されます
2008

49
レガシーコード以外ではrawタイプを使用しないでください。を使用しArrayList<String> newArrayList = (ArrayList<String>) oldArrayList.clone();たほうがよいです。
cdmckay 2009年

20
ArrayListには#cloneメソッドがありますが、List自体にはありません。はぁ。
rogerdpack 2012

12
関連はありませんが、その間:左側のList<String>代わりに使用しますArrayList<String>。これは、ほとんどの場合コレクションを使用する方法です。
Christophe Roussy 2013年

3
このメソッドを使用してArrayListを複製できませんでした。clone()メソッドは認識されませんでした。
ジャック

318

なぜクローンを作成するのですか?通常、新しいリストを作成する方が理にかなっています。

List<String> strs;
...
List<String> newStrs = new ArrayList<>(strs);

仕事が終わりました。


17
リストの種類がわからない場合があります。これは、LinkedList、MyOwnCustomList、またはArrayListのサブクラスである場合があります。その場合、ArrayListの新規作成は不適切なタイプになります。
スティーブKuo

51
元のリストがどの実装を使用したか気になりますか?新しいリストがどの実装を使用するかはおそらく気になります。
トム・ホーティン-2008年

12
@Steve Kuo:署名とはArrayList(Collection<? extends E> c)、引数として使用するリストの種類が問題ではないことを意味します。
cdmckay 2009年

3
深いコピーではないということですか?

4
@YekhezkelYovelいいえ、そうではありません。
トム・ホーティン-タックライン

19

これは私がそのために使用するコードです:

ArrayList copy = new ArrayList (original.size());
Collections.copy(copy, original);

希望はあなたにとって便利です


3
生の型の使用は避けてください。ArrayList使用する代わりにArrayList<YourObject>
milosmns '17

2
docs.oracle.com/javase/8/docs/api/java/util/… リストのサイズが異なるため機能しません。
MLProgrammer-CiM 2016年

なぜArrayListコンストラクタのコピーオーバーロードを使用しないのですか?
トムホーティン-タックライン

19

Java 8では、ストリームを使用してクローンを作成できます。

import static java.util.stream.Collectors.toList;

...

List<AnObject> clone = myList.stream().collect(toList());

List <AnObject> xy = new ArrayList <>(oldList);のように実行できます。
mirzak

1
これはディープコピーではありません。あるリストの要素の変更は別のリストでも確認できます
Inchara

2
問題のどこにディープコピーが必要かを指定していますか?同じオブジェクトを含む別のコレクションが必要であると想定しています。ディープコピーのルートをたどる場合は、まったく新しいワームの缶を開いていることになります。
Simon Jenkins

実際にはクローンではありませんが、そうですか?APIドキュメントから「返されるリストのタイプ、変更可能性、直列化可能性、またはスレッドセーフ性は保証されません」。えw、そのうちの1つは欲しくない。toCollectionより良い選択かもしれません。
トムホーティン-

16

Object.clone()にはいくつかの大きな問題があり、ほとんどの場合、その使用はお勧めできません。完全な回答については、Joshua Blochによる「Effective Java」の項目11を参照してください。プリミティブ型の配列でObject.clone()を安全に使用できると思いますが、それとは別に、クローンの適切な使用とオーバーライドについて慎重に考える必要があります。おそらく、セマンティクスに従ってオブジェクトを明示的に複製するコピーコンストラクターまたは静的ファクトリーメソッドを定義したほうがよいでしょう。


15

これは、コレクションAPIを使用してうまくいくはずです。

:copyメソッドは線形時間で実行されます。

//assume oldList exists and has data in it.
List<String> newList = new ArrayList<String>();
Collections.copy(newList, oldList);

13
新しいArrayList <String>(oldList)を使用しないのはなぜですか?
cdmckay 2009年

4
docからこれは機能しないと思います*宛先リストは、少なくともソースリストと同じ長さである必要があります。それより長い場合、宛先リストの残りの要素は影響を受けません。
Greg Domjan

@GregDomjanは、これが最善の方法であることに同意するわけではありませんが、それを行う方法です。問題を回避するには、次のように簡単です:List <String> newList = new ArrayList <>(oldList.size());
nckbrz 14年

1
それがJava 8に関連しているかどうかはわかりませんが、サイズを指定しても例外が発生しますIndexOutOfBoundsException:destination.size()<source.size():0 <2 on List<MySerializableObject> copyList = new ArrayList<>(mMySerializableObjects.size()); 使用するのcopyList.addAll(original);が良い代替案のようです
Gene Bo

@GeneBoサイズを指定していません。これは容量を指定するもので、非常に異なります。謎のint議論の喜び。古き良きの代わりに、このあいまいな静的メソッドを使用する理由がわかりませんaddAll
トム・ホーティン-

8

addAllを使用すると問題なく動作することがわかりました。

ArrayList<String> copy = new ArrayList<String>();
copy.addAll(original);

ジェネリック構文ではなく括弧が使用されます


5
これは文字列では機能しますが、変更可能なオブジェクトでは機能しません。それらも複製する必要があります。
jodonnell 2008

ええ、彼の質問は弦です。そして、彼はこの場合の鋳造物が本当に好きではないジェネリックの問題を抱えています。
Allain Lalonde、

また、ArrayList.cloneは浅い複製のみを実行するため、リスト内の変更可能なオブジェクトはそのメソッドを使用して複製されません。
2008

それはArrayList <String>です。また、新しいArrayList <String>(original)は、記述が少なく、同じくらい明確であるため、おそらく使用した方がよいでしょう。
cdmckay 2009年

4
なぜ使用しないのnew ArrayList<String>(original)ですか?
user102008

6

ゲッターでリストを返すことができるようにこれが必要な場合は、次のようにすることをお勧めします。

ImmutableList.copyOf(list);

4

汎用インターフェースを複製するにjava.util.Listは、キャストする必要があります。ここにあなたは例です:

List list = new ArrayList();
List list2 = ((List) ( (ArrayList) list).clone());

これは少しトリッキーですが、Listインターフェースを返すように制限されている場合は機能します。そのため、リストを実装した後の誰でも、いつでも好きなときに実装できます。

私はこの答えが最終的な答えに近いことを知っていますが、私の答えはあなたがList-一般的な親-で作業しているときにそれをすべて行う方法に答えますArrayList


2
あなたはそれが常にtrueではないArrayListであると仮定しています
jucardi

@JuanCarlosDiazは、これは質問された質問であり、ArrayListに関するものだったので、ArrayListに対して回答しました:)
Ahmed Hamdy

2

ArrayListのクローンを作成するときは十分注意してください。Javaでのクローニングは浅いです。つまり、Arraylist自体のみを複製し、そのメンバーは複製しません。したがって、ArrayList X1があり、それをX2に複製すると、X2の変更はX1にも現れ、その逆も同様です。複製すると、元の同じ要素へのポインタを持つ新しいArrayListのみが生成されます。


2

これも機能するはずです:

ArrayList<String> orig = new ArrayList<String>();
ArrayList<String> copy = (ArrayList<String>) orig.clone()

2
List<String> shallowClonedList = new ArrayList<>(listOfStrings);

これは深いコピーではなく、浅いコピーであることを覚えておいてください。新しいリストを取得しますが、エントリは同じです。これは単に文字列の場合は問題ありません。リストのエントリ自体がオブジェクトである場合、Getはよりトリッキーです。


1
ArrayList first = new ArrayList ();
ArrayList copy = (ArrayList) first.clone ();

1

私はJavaの専門家ではありませんが、同じ問題があり、この方法で解決しようとしました。(Tにコピーコンストラクタがあると仮定します)。

 public static <T extends Object> List<T> clone(List<T> list) {
      try {
           List<T> c = list.getClass().newInstance();
           for(T t: list) {
             T copy = (T) t.getClass().getDeclaredConstructor(t.getclass()).newInstance(t);
             c.add(copy);
           }
           return c;
      } catch(Exception e) {
           throw new RuntimeException("List cloning unsupported",e);
      }
}

List実装クラスに引数なしのパブリックコンストラクターがあることは保証されません。例えば、ListsのリターンによってArray.asListList.ofまたはList.subListおそらくにはありません。
トム・ホーティン-
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.