ArrayListを複製し、その内容を複製する方法は?


273

ArrayListJavaでのクローンを作成し、そのアイテムをクローンするにはどうすればよいですか?

たとえば、私は持っています:

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

そして、私はのオブジェクトclonedListが犬のリストと同じではないことを期待します。


回答:


199

アイテムを反復処理し、1つずつクローンを作成して、結果の配列にクローンを配置する必要があります。

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()メソッドをオーバーライドする必要があります。


19
ただし、一般的にはできません。clone()はCloneableインターフェースの一部ではありません。
マイケルマイヤーズ

13
ただし、clone()はObjectで保護されているため、アクセスできません。そのコードをコンパイルしてみてください。
マイケルマイヤーズ

5
すべてのクラスはObjectを拡張するため、clone()をオーバーライドできます。これがCloneableの目的です!
Stephan202

2
これは良い答えです。Cloneableは実際にはインターフェースです。ただし、mmyersには、clone()メソッドがObjectクラスで宣言された保護されたメソッドであるという点があります。Dogクラスでこのメソッドをオーバーライドし、手動でフィールドのコピーを行う必要があります。
ホセ

3
つまり、Dogのインスタンスを取得するファクトリーまたはビルダー、または単に静的メソッドを作成し、フィールドを新しいインスタンスに手動でコピーして、その新しいインスタンスを返します。
ホセ

196

私は個人的に、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インターフェイスを作成してそれを使用することです。このようにして、クローン作成のための汎用メソッドを作成できます。


DOGのすべてのフィールドをコピーして、より具体的にできますか。私は本当に理解していません:(
Dr. aNdRO 2014

(Dogの代わりに)未定義のオブジェクトに対してその関数を書くことは可能ですか?
Tobi G.

@TobiG。どういう意味かわかりません。あなたがしたいですcloneList(List<Object>)Dog(Object)
cdmckay 2016

@cdmckay cloneList(List <Object>)、cloneList(List <Dog>)およびcloneList(List <Cat>)で機能する1つの関数。しかし、私が推測する一般的なコンストラクターを呼び出すことはできません...?
Tobi G.

@TobiG。では、一般的なクローン機能のように?それは、この質問が実際に何についてであるかではありません。
cdmckay 2016

143

すべての標準コレクションには、コピーコンストラクタがあります。それらを使用してください。

List<Double> original = // some list
List<Double> copy = new ArrayList<Double>(original); //This does a shallow copy

clone()は、いくつかの間違い(この質問を参照)で設計されたので、それを避けるのが最善です。

以下からの効果的なJavaの第2版、項目11:オーバーライドクローン慎重

Cloneableに関連するすべての問題を考えると、他のインターフェースはそれを拡張してはならず、継承用に設計されたクラス(項目17)はそれを実装すべきではないと言っても安全です。多くの欠点があるため、一部のエキスパートプログラマーは、クローンメソッドをオーバーライドせず、配列をコピーする場合を除いて、クローンメソッドを呼び出さないことを選択しています。継承用のクラスを設計する場合、適切に動作する保護されたクローンメソッドを提供しないことを選択すると、サブクラスがCloneableを実装することが不可能になることに注意してください。

この本では、コピーコンストラクターがCloneable / cloneを超える多くの利点についても説明しています。

  • リスクが発生しやすい言語外のオブジェクト作成メカニズムに依存しない
  • 彼らは、薄く文書化された慣習への強制できない遵守を要求しません
  • 彼らはファイナルフィールドの適切な使用と競合しません
  • それらは不必要なチェック例外をスローしません
  • キャストは必要ありません。

コピーコンストラクターを使用する別の利点を検討してください:がありHashSet s、それをとしてコピーするとしTreeSetます。cloneメソッドはこの機能を提供できませんが、変換コンストラクターを使用すると簡単ですnew TreeSet(s)


85
私が知っている限りでは、標準コレクションのコピーコンストラクターは、深いコピーではなく、浅いコピーを作成します。ここで尋ねられる質問は、深いコピーの答えを探します。
Abdull 2012年

20
これは単に間違っています。コピーの作成者は浅いコピーを実行します-問題の
全体像

1
この回答の正しいところは、リスト内のオブジェクトを変更しない場合、アイテムを追加または削除しても、両方のリストからそれらが削除されないことです。単純な割り当てほど浅くはありません。
Noumenon

42

Java 8は、要素dogsのコピーコンストラクターまたはクローンメソッドをエレガントかつコンパクトに呼び出す新しい方法を提供します:Streamslambdasおよびcollectors

コピーコンストラクタ:

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.*;ます。


ArrayListSのコレクター

最後の例のコレクターは、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 */ { ...

とにかく、これはベストプラクティスであるため、これは大きな問題にはなりません。(例えばEffectice Javaがこのアドバイスを提供します。)

これを指摘してくれたGustavoに感謝します。


PS:

見栄えが良い場合は、代わりにメソッド参照構文を使用してまったく同じことを実行できます。

List<Dog> clonedDogs = dogs.stream().map(Dog::clone).collect(toList());

Dog(d)がコピーコンストラクターである場合に、この方法を実行するとパフォーマンスに影響がありますか? List<Dog> clonedDogs = new ArrayList<>(); dogs.stream().parallel().forEach(d -> clonedDogs.add(new Dog(d)));
SaurabhJinturkar

1
@SaurabhJinturkar:このバージョンはスレッドセーフではないため、並列ストリームで使用しないでください。これは、parallel呼び出しがclonedDogs.add複数のスレッドから同時に呼び出されるためです。を使用collectするバージョンはスレッドセーフです。これは、ストリームライブラリの機能モデルの利点の1つです。同じコードを並列ストリームに使用できます。
Lii

1
@SaurabhJinturkar:また、収集操作は高速です。バージョンとほとんど同じですが、並列ストリームでも機能します。たとえば、配列リストの代わりに並行キューを使用してバージョンを修正することもできますが、かなり遅くなることはほぼ確実です。
Lii

私があなたの解決策を使おうとするときはいつでも、私はUnhandled exception type CloneNotSupportedExceptionオンになりd.clone()ます。例外を宣言またはキャッチしても解決されません。
グスタボ

@Gustavo:ほぼ確実に、クローンを作成しているオブジェクト(Dogこの例では)はクローンをサポートしていません。それはClonableインターフェースを実装していますか?
Lii

28

基本的に、手動で繰り返すことなく3つの方法があります。

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

注意:指定されたコレクションが操作の進行中に変更された場合、これらの操作の動作は未定義になります。


50
これらの3のすべてが唯一作成変種ではないことをしてください浅いコピーリストのを
electrobabe

9
これはディープクローンではありません。これらの2つのリストは同じオブジェクトを保持し、参照のみをコピーしましたが、Dogオブジェクトです。どちらかのリストを変更すると、次のリストも同じように変更されます。そんなに多くの賛成票はありません。
Saorikido

1
@ Neeson.Zすべてのメソッドは、リストの深いコピーとリストの要素の浅いコピーを作成します。リストの要素を変更すると、その変更は他のリストに反映されますが、リストの1つを変更すると(オブジェクトの削除など)、他のリストは変更されません。
Alessandro Teruzzi

17

私は現在の緑の答えは悪いと思います、なぜあなたは尋ねるかもしれませんか?

  • 多くのコードを追加する必要がある場合があります
  • コピーするすべてのリストをリストしてこれを行う必要があります

シリアライゼーションも悪い方法ですが、どこでもSerializableを追加する必要があるかもしれません。

だから解決策は何ですか?

Javaディープクローンライブラリ クローンライブラリは、オブジェクトをディープクローンする小さなオープンソース(Apacheライセンス)Javaライブラリです。オブジェクトはCloneableインターフェースを実装する必要はありません。事実上、このライブラリは任意のJavaオブジェクトを複製できます。キャッシュされたオブジェクトを変更したくない場合や、オブジェクトのディープコピーを作成したい場合は、キャッシュ実装で使用できます。

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

https://github.com/kostaskougios/cloningで確認してください。


8
この方法の注意点の1つは、反射を使用することです。これは、バルカンのソリューションよりもかなり遅くなる可能性があります。
cdmckay

6
「たくさんのコードが必要」という最初のポイントがわかりません。あなたが話しているライブラリは、より多くのコードを必要とするでしょう。それをどこに置くかという問題だけです。そうでなければ、私はこの種のもののための特別なライブラリを支援合意...
nawfal

8

私は方法を見つけました、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());

ジャクソンと他のjsonライブラリを使用してそれを行うこともできます。


1
なぜ人々がこの回答に反対票を投じたのかはわかりません。その他の答えは、clone()を実装するか、依存関係を変更して新しいライブラリを含める必要があります。しかし、JSonライブラリのほとんどのプロジェクトにはすでに含まれています。私はこれに賛成しました。
サティシュ2018年

1
@Satishはい、これは私を助けた唯一の答えです、私は他の人の何が悪いのかわかりませんが、私が何をしたか、コピーコンストラクタを複製または使用しても、私の元のリストは更新されていましたが、この方法ではそれはできません、それで作者に感謝します!
Parag Pawar 2018

まあ、それは知識のための純粋なJavaの返信ではなく、この問題を迅速に処理するための効率的なソリューションです
marcRDZ

素晴らしいハック、時間の節約
mxml

6

私は常にこのオプションを使用してきました:

ArrayList<Dog> clonedList = new ArrayList<Dog>(name_of_arraylist_that_you_need_to_Clone);

2

あなたはクローンを作成する必要がありますArrayList(これを反復して、新しいの各要素をコピーすることによって、手作業でArrayListあるため、)clone()あなたのためにそれを行うことはありません。これは、に含まれるオブジェクトがそれ自体をArrayList実装Clonableしない可能性があるためです。

編集:...そしてそれはまさにVarkhanのコードが行うことです。


1
そして、たとえそうであっても、リフレクション以外にclone()にアクセスする方法はなく、とにかく成功するとは限りません。
マイケルマイヤーズ

1

厄介な方法は、反射でそれを行うことです。このようなことがうまくいきました。

public static <T extends Cloneable> List<T> deepCloneList(List<T> original) {
    if (original == null || original.size() < 1) {
        return new ArrayList<>();
    }

    try {
        int originalSize = original.size();
        Method cloneMethod = original.get(0).getClass().getDeclaredMethod("clone");
        List<T> clonedList = new ArrayList<>();

        // noinspection ForLoopReplaceableByForEach
        for (int i = 0; i < originalSize; i++) {
            // noinspection unchecked
            clonedList.add((T) cloneMethod.invoke(original.get(i)));
        }
        return clonedList;
    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
        System.err.println("Couldn't clone list due to " + e.getMessage());
        return new ArrayList<>();
    }
}

きちんとした、厄介なトリック!1つの潜在的な問題:original異なるクラスのオブジェクトが含まれている場合cloneMethod.invoke、間違った種類のオブジェクトで呼び出されると例外で失敗すると思います。このため、Methodオブジェクトごとに特定のクローンを取得する方がよい場合があります。または、クローンメソッドを使用しますObject(ただし、保護されているため、失敗する可能性が高くなります)。
Lii

また、空のリストを返すよりも、catch-clauseで実行時例外をスローする方がよいと思います。
Lii

1
List<Dog> dogs;
List<Dog> copiedDogs = dogs.stream().map(dog -> SerializationUtils.clone(dog)).Collectors.toList());

これは各犬を深くコピーします


0

他のポスターは正解です。リストを反復処理して、新しいリストにコピーする必要があります。

ただし...リスト内のオブジェクトが不変である場合-クローンを作成する必要はありません。オブジェクトに複雑なオブジェクトグラフがある場合-それらも不変である必要があります。

不変性の他の利点は、スレッドセーフでもあることです。


0

一般的なテンプレートタイプを使用したソリューションを次に示します。

public static <T> List<T> copyList(List<T> source) {
    List<T> dest = new ArrayList<T>();
    for (T item : source) { dest.add(item); }
    return dest;
}

ジェネリックは良いですが、質問に答えるためにアイテムを複製する必要もあります。stackoverflow.com/a/715660/80425を
David Snabel-Caunt

0

オブジェクトのためにclone()メソッドをオーバーライドします

class You_class {

    int a;

    @Override
    public You_class clone() {
        You_class you_class = new You_class();
        you_class.a = this.a;
        return you_class;
    }
}

そして、Vector objまたはArraiList obj ....に対して.clone()を呼び出します


0

commons-lang-2.3.jarを使用して、Javaのライブラリがリストを複製する簡単な方法

リンクダウンロードcommons-lang-2.3.jar

使い方

oldList.........
List<YourObject> newList = new ArrayList<YourObject>();
foreach(YourObject obj : oldList){
   newList.add((YourObject)SerializationUtils.clone(obj));
}

これがお役に立てば幸いです。

:D


1
ただメモ:なぜそのような古いバージョンのCommons Langなのか?リリース履歴はこちらからご覧ください:commons.apache.org/proper/commons-lang/release-history.html
informatik01

0

その包み import org.apache.commons.lang.SerializationUtils;

方法があります SerializationUtils.clone(Object);

this.myObjectCloned = SerializationUtils.clone(this.object);

この質問に答えるのは少し時代遅れです。そして質問の下のコメントで他の多くの答え。
moskito-x 2013


0

以下は私のために働いた。

Dog.java

public Class Dog{

private String a,b;

public Dog(){} //no args constructor

public Dog(Dog d){ // copy constructor
   this.a=d.a;
   this.b=d.b;
}

}

 -------------------------

 private List<Dog> createCopy(List<Dog> dogs) {
 List<Dog> newDogsList= new ArrayList<>();
 if (CollectionUtils.isNotEmpty(dogs)) {
 dogs.stream().forEach(dog-> newDogsList.add((Dog) SerializationUtils.clone(dog)));
 }
 return newDogsList;
 }

ここでは、createCopyメソッドから作成された新しいリストがSerializationUtils.clone()を介して作成されます。したがって、新しいリストに加えられた変更は、元のリストには影響しません


-1

ディープコピーArrayListを作成する本当に簡単な方法を見つけたと思います。String ArrayList arrayAをコピーするとします。

ArrayList<String>arrayB = new ArrayList<String>();
arrayB.addAll(arrayA);

うまくいかない場合はお知らせください。


3
たとえば私の場合、List <List <JsonObject >>を使用すると機能しません
djdance

文字列は不変です。クローニングは意味がなく、あなたの例ではarrayBとarrayAは同じオブジェクト参照を持っています-それは浅いコピーです。
クリスチャンフライド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.