Bir dizinin kopyasını oluşturma


345

aSürekli güncellenen bir dizi var . Diyelim a = [1,2,3,4,5]. Tam bir kopyasını almam ave çağırmam gerekiyor b. Eğer adeğişecek olsaydı [6,7,8,9,10], bhala olmalı [1,2,3,4,5]. Bunu yapmanın en iyi yolu nedir? forGibi bir döngü denedim :

for(int i=0; i<5; i++) {
    b[i]=a[i]
}

ancak bu düzgün çalışmıyor gibi görünüyor. Lütfen derin kopya gibi gelişmiş terimleri kullanmayın, çünkü bunun ne anlama geldiğini bilmiyorum.

Yanıtlar:


559

System.arraycopy () kullanmayı deneyebilirsiniz

int[] src  = new int[]{1,2,3,4,5};
int[] dest = new int[5];

System.arraycopy( src, 0, dest, 0, src.length );

Ancak, çoğu durumda clone () kullanmak daha iyidir:

int[] src = ...
int[] dest = src.clone();

9
Tekerleği yeniden yerleştirmediği için +1. Ve bildiğim kadarıyla, bu çözüm dizi kopyalamaya daha hızlı ulaşabilirsiniz.
Felipe Hummel

6
hem klon hem de dizikopi doğaldır. Klonun marjinal olarak daha hızlı olmasını beklerdim. fark önemli değil.
MeBigFatGuy

5
@Felipe, @MeBigFatGuy - sadece büyük bir dizi için. Küçük bir dizi için, kurulum ek yükleri nedeniyle bir kopyalama döngüsü daha hızlı olabilir. Javadoc'a bakarsanız System.arraycopy, yöntemin başlamadan önce çeşitli şeyleri kontrol etmesi gerektiğini görürsünüz. Bu kontrollerden bazıları, statik dizi türlerine bağlı olarak bir kopyalama döngüsü ile gereksizdir.
Stephen C

7
@FelipeHummel, @MeBigFatGuy, @StephenC - Buradaki cevaplarda belirtilen dizi kopyalama yöntemlerinin performans testi . Bu kurulumda, clone()250 000 eleman için en hızlı olduğu ortaya çıkıyor.
Adam

6
Buradaki tüm tartışmaların, zamanın% 99,999'unun önemli olmadığı mikro performans sorunları hakkında olduğunu görmek hayal kırıklığı yaratıyor. Daha önemli olan nokta, src.clone()daha okunabilir olması ve yeni bir dizi ayırmaktan ve yapmaktan daha az hata yapma şansına sahip olmasıdır arraycopy. (Ve ayrıca hızlı olur.)
Brian Goetz

231

kullanabilirsiniz

int[] a = new int[]{1,2,3,4,5};
int[] b = a.clone();

de.


6
Sadece OP'nin şu noktayı temizliyorum: " A [6,7,8,9,10] olarak değişecek olsaydı, B yine de [1,2,3,4,5] olmalı ”. OP döngü kullanmayı denediğini ama onun için çalışmadığını söyledi.
Harry Joy

15
Oyuncular gereksizdir; iyi bir statik analizör bu konuda uyarır. Ancak klonlama kesinlikle bir dizinin yeni bir kopyasını oluşturmanın en iyi yoludur.
erickson

5
@ MeBigFatGuy - OP'nin kullanım durumu aynı diziye tekrar tekrar kopyalanmasını gerektirir, bu nedenle klon çalışmaz.
Stephen C

4
@Stephen C, bunu okumadım. Sadece bir kopyasını istediğini okudum ve daha sonra tekrar tekrar saklanmayan sürümü güncelleyecek.
MeBigFatGuy

4
@ MeBigFatGuy - "Sürekli güncellenen bir A dizim var." Dedi . . Belki de çok fazla okuyorum ama bunu tekrar tekrar A'dan B'ye kopyaladığını ima etmek olarak kabul ediyorum.
Stephen C

184

Şunun bir kopyasını oluşturmak istiyorsanız:

int[] a = {1,2,3,4,5};

Bu yol:

int[] b = Arrays.copyOf(a, a.length);

Arrays.copyOfa.clone()küçük dizilerden daha hızlı olabilir . Her iki kopya öğesi de eşit derecede hızlı ancak clone () döndürür, Objectböylece derleyici örtük bir döküm eklemek zorundadır int[]. Bayt kodunda, bunun gibi bir şey görebilirsiniz:

ALOAD 1
INVOKEVIRTUAL [I.clone ()Ljava/lang/Object;
CHECKCAST [I
ASTORE 2

63

Http://www.journaldev.com/753/how-to-copy-arrays-in-java adresinden güzel açıklama

Java Dizisi Kopyalama Yöntemleri

Object.clone () : Object sınıfı clone () yöntemi sağlar ve java içindeki dizi de bir Object olduğundan, tam dizi kopyasını elde etmek için bu yöntemi kullanabilirsiniz. Dizinin kısmi kopyasını almak istiyorsanız bu yöntem size uymayacaktır.

System.arraycopy () : Sistem sınıfı arraycopy (), bir dizinin kısmi kopyasını yapmanın en iyi yoludur. Kopyalanacak toplam öğe sayısını ve kaynak ve hedef dizi dizin konumlarını belirtmenin kolay bir yolunu sunar. Örneğin, System.arraycopy (kaynak, 3, hedef, 2, 5) kaynağın 3. dizininden hedefin 2. dizinine başlayarak 5 öğeyi kaynaktan hedefe kopyalar.

Arrays.copyOf (): Bir dizinin ilk birkaç öğesini veya dizinin tam kopyasını kopyalamak istiyorsanız, bu yöntemi kullanabilirsiniz. Açıkçası System.arraycopy () gibi çok yönlü değil, aynı zamanda kafa karıştırıcı ve kullanımı kolay değil.

Arrays.copyOfRange () : Başlangıç ​​dizininin 0 olmadığı bir dizinin birkaç öğesinin kopyalanmasını istiyorsanız, kısmi diziyi kopyalamak için bu yöntemi kullanabilirsiniz.


35

Tüm bu "bir dizi kopyalamak için daha iyi yollar" gerçekten sorununuzu çözmeyecek bir duygu var.

Diyorsun

[...] gibi bir for döngüsü denedim ama bu düzgün çalışmıyor gibi görünüyor?

Bu döngüye bakıldığında, çalışmaması için açık bir neden yoktur ...

  • ave bdizilerin bir şekilde dağılmasını sağlayın (ör. ave baynı diziye bakın) veya
  • uygulamanız çok iş parçacıklı ve farklı iş parçacıkları aaynı anda diziyi okuyor ve güncelliyor .

Her iki durumda da, kopyalama yapmanın alternatif yolları altta yatan sorunu çözmez.

İlk senaryo için düzeltme açıktır. İkinci senaryoda, evreleri senkronize etmenin bir yolunu bulmanız gerekecektir. Atomik dizi sınıfları, atomik kopya yapıcıları veya klon yöntemleri olmadığından yardımcı olmazlar, ancak ilkel bir muteks kullanarak senkronize etmek hile yapar.

(Sorunuzda bunun gerçekten konu ile ilgili olduğunu düşünmemi sağlayan ipuçları var; örneğin asürekli değişen ifadeniz.)


2
kabul etti .. muhtemelen doğru.
11:04


9

Diziden uzunluk çağıran tüm çözüm, kodunuzu gereksiz boş denetleyicileri ekleyin

int[] a = {1,2,3,4,5};
int[] b = Arrays.copyOf(a, a.length);
int[] c = a.clone();

//What if array a comes as local parameter? You need to use null check:

public void someMethod(int[] a) {
    if (a!=null) {
        int[] b = Arrays.copyOf(a, a.length);
        int[] c = a.clone();
    }
}

Tekerleği icat etmemenizi ve gerekli tüm kontrollerin zaten yapıldığı hizmet sınıfını kullanmanızı tavsiye ederim. Apache ortaklarından ArrayUtils'i düşünün. Kod kısalır:

public void someMethod(int[] a) {
    int[] b = ArrayUtils.clone(a);
}

Orada bulabileceğiniz Apache müşterileri


8

Ayrıca kullanabilirsiniz Arrays.copyOfRange.

Örnek :

public static void main(String[] args) {
    int[] a = {1,2,3};
    int[] b = Arrays.copyOfRange(a, 0, a.length);
    a[0] = 5;
    System.out.println(Arrays.toString(a)); // [5,2,3]
    System.out.println(Arrays.toString(b)); // [1,2,3]
}

Bu yöntem benzer Arrays.copyOf, ancak daha esnektir. İkisi System.arraycopyde kaputun altında kullanıyor .

Bakınız :


3

Bir dizinin sıfırdan güvenli bir kopyası için, Object.clone()bu yanıtta sağlanan yöntemle isteğe bağlı olarak da kullanabilirsiniz .

int[] arrayToCopy = {1, 2, 3};
int[] copiedArray = Optional.ofNullable(arrayToCopy).map(int[]::clone).orElse(null);

Bu çözümün aşırı karmaşık olmasına rağmen, aynı zamanda bellek israfını da beraberinde getirir ve dizi bir gizli (örn. Şifre ile bayt dizisi) içeriyorsa, güvenlik kusurları da sağlar, çünkü ara nesneler çöp toplama ve maruz kalana kadar yığın üzerinde kalacaktır. saldırganlara.
Weltraumschaf

1
Dizinin bu yapı için özel olarak yığın üzerinde olacağını kabul etmiyorum. Gerçekten de klonlamayı yalnızca gerektiğinde çağırır ve Optionalnesne sadece mevcut diziye referansla boş bir nesnedir. Performans etkisi hakkında, bu tür bir yapının JVM içinde satır içi oluşturmak için iyi bir aday olduğu ve daha sonra diğer yöntemlerden daha fazla etki olmadığı için bunun aslında bir etki olduğunu söylemenin erken olduğunu söyleyebilirim. Daha karmaşık ya da değil olarak düşünmek bir stil meselesidir (prosedürel programlamaya karşı işlevsel programlama, sadece değil).
Nicolas Henneaux

3

Eğer değil çiğ dizilerle çalışmak ve gerekiyorsa ArrayListo zaman Arraysneye ihtiyacınız vardır. Kaynak koduna bakarsanız, bunlar bir dizinin kopyasını almanın kesinlikle en iyi yoludur. İyi bir savunma programlaması var çünküSystem.arraycopy()Mantıksal parametreleri beslerseniz yöntem çok sayıda kontrolsüz istisna atar .

Arrays.copyOf()İlk Nthöğeden öğeye yeni kısa diziye kopyalanacak olan herhangi birini kullanabilirsiniz .

public static <T> T[] copyOf(T[] original, int newLength)

Belirtilen diziyi keser veya doldurma işlemini null değerleriyle (gerekirse) kopyalar, böylece kopyanın belirtilen uzunluğu olur. Hem orijinal dizide hem de kopyada geçerli olan tüm dizinler için, iki dizi aynı değerleri içerecektir. Kopyada geçerli olan ancak orijinalde olmayan tüm dizinler için, kopya null içerir. Bu tür indeksler, yalnızca belirtilen uzunluk orijinal dizininkinden daha büyükse ve mevcut olacaktır. Sonuçta elde edilen dizi, orijinal diziyle tam olarak aynı sınıftadır.

2770
2771    public static <T,U> T[] More ...copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
2772        T[] copy = ((Object)newType == (Object)Object[].class)
2773            ? (T[]) new Object[newLength]
2774            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
2775        System.arraycopy(original, 0, copy, 0,
2776                         Math.min(original.length, newLength));
2777        return copy;
2778    }

ya Arrays.copyOfRange()da hile yapacak:

public static <T> T[] copyOfRange(T[] original, int from, int to)

Belirtilen dizinin belirtilen aralığını yeni bir diziye kopyalar. Aralığın başlangıç ​​endeksi (itibaren) sıfır ile orijinal arasında olmalıdır. Uzunluk, dahil. Original [from] öğesindeki değer, kopyanın ilk öğesine yerleştirilir (== original.length veya == - arasında değilse). Orijinal dizideki sonraki öğelerin değerleri, kopyadaki sonraki öğelere yerleştirilir. Aralıktan büyük veya ona eşit olması gereken son dizinin orijinal dizini, orijinalden daha büyük olabilir. uzunluk - itibaren. Döndürülen dizinin uzunluğu - arasında olacaktır. Sonuçta elde edilen dizi, orijinal diziyle tam olarak aynı sınıftadır.

3035    public static <T,U> T[] More ...copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
3036        int newLength = to - from;
3037        if (newLength < 0)
3038            throw new IllegalArgumentException(from + " > " + to);
3039        T[] copy = ((Object)newType == (Object)Object[].class)
3040            ? (T[]) new Object[newLength]
3041            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
3042        System.arraycopy(original, from, copy, 0,
3043                         Math.min(original.length - from, newLength));
3044        return copy;
3045    }

Gördüğünüz gibi, bunların her ikisi de System.arraycopy, yapmaya çalıştığınız şeyin geçerli olduğu savunma mantığıyla sadece sarmalayıcı işlevleridir .

System.arraycopy dizileri kopyalamanın en hızlı yoludur.


0

2D dizilerle de benzer bir sorun yaşadım ve burada bitirdim. Ana diziyi kopyalayıp iç dizilerin değerlerini değiştiriyordum ve değerler her iki kopyada da değiştiğinde şaşırdım. Temel olarak her iki kopya da bağımsızdı ancak aynı iç dizilere referanslar içeriyordu ve istediğim şeyi elde etmek için iç dizilerin bir dizi kopyasını yapmak zorunda kaldım.

Muhtemelen OP'nin sorunu değil, ama umarım yine de yardımcı olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.