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()
は私の頭の中ではまったく役に立たない関数になります。だから、コピー機能をプログラミングすることを除いて(これを行うつもりです)、これを行う適切な方法はありますか?
呼び出し中
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で不変)であるString
sの場合、ディープコピーは必要ありません。 MySpecialObject
の場合、その深いコピーを作成する方法を知る必要がありますが、これは一般的な操作ではありません。
注:最初に受け入れられた答えは、GoogleでのCollections.copy
の上位結果であり、コメントで指摘されているように間違っていました。
b
のcapacityは3ですが、sizeは0です。 ArrayList
が何らかのバッファ容量を持っているという事実は実装の詳細です-List
インターフェイスの一部ではないため、Collections.copy(List, List)
は使用しません。特殊な場合ArrayList
を使用するのはいでしょう。
MrWigglesが示したように、コレクションを取得するArrayListコンストラクターを使用することは、提供されている例での方法です。
より複雑なシナリオ(実際のコードが含まれる場合もあります)の場合は、 Google Javaコレクションライブラリ が便利です。
ただやる:
List a = new ArrayList();
a.add("a");
a.add("b");
a.add("c");
List b = new ArrayList(a);
ArrayListには、別のコレクションを受け入れて要素をコピーするコンストラクターがあります
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()を使用して、アイテムを手動で繰り返す必要があります。
リストをコピーする最も簡単な方法は、リストを新しいリストのコンストラクターに渡すことです。
List<String> b = new ArrayList<>(a);
b
はa
の浅いコピーになります
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'
List b = new ArrayList(a.size())
サイズを設定しません。初期容量を設定します(サイズを変更する前に収まる要素の数)。この場合のコピーの簡単な方法は次のとおりです。
List b = new ArrayList(a);
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.
}
}
}
なぜ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
ここでのほとんどの答えは問題を認識しません。ユーザーは最初のリストから2番目のリストへの要素のコピーを望みます。宛先リストの要素は新しいオブジェクトであり、元のリストの要素への参照ではありません。 (2番目のリストの要素を変更しても、ソースリストの対応する要素の値は変更されません。)可変オブジェクトの場合、ArrayList(Collection)コンストラクターを使用できません。コピーするときは、オブジェクトごとにリストクローンを作成する必要があります。
private List<Item> cloneItemList(final List<Item> items)
{
Item[] itemArray = new Item[items.size()];
itemArray = items.toArray(itemArray);
return Arrays.asList(itemArray);
}
文字列は、
List<String> b = new ArrayList<String>(a);
それらは不変だからです。他のすべてのオブジェクトではなく、自分で繰り返しコピーを行う必要があります。
ArrayListをコピーする場合は、次を使用してコピーします。
List b = new ArrayList();
b.add("aa");
b.add("bb");
List a = new ArrayList(b);
次の出力は、コピーコンストラクターと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()の動作を確認するには出力が十分です。
他のすべてのオブジェクトではなく、自分で繰り返しコピーを行う必要があります。
これを回避するには、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);
}
*/
}
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())
そして、Google guavaを使用している場合、1行のソリューションは
List<String> b = Lists.newArrayList(a);
これにより、可変配列リストのインスタンスが作成されます。
いくつかの値を既存のコレクションにコピーするユースケースを想像するなら、コピーは役に立たないわけではありません。つまり挿入するのではなく、既存の要素を上書きします。
例: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