Buat salinan array

345

Saya memiliki array ayang terus diperbarui. Katakan saja a = [1,2,3,4,5]. Saya perlu membuat salinan duplikat yang tepat adan menyebutnya b. Jika aingin berubah [6,7,8,9,10], bharus tetap [1,2,3,4,5]. Apa cara terbaik untuk melakukan ini? Saya mencoba forloop seperti:

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

tapi itu sepertinya tidak berfungsi dengan benar. Tolong jangan gunakan istilah lanjutan seperti salinan dalam, dll., Karena saya tidak tahu apa artinya itu.

badcoder
sumber

Jawaban:

558

Anda dapat mencoba menggunakan System.arraycopy ()

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

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

Tapi, mungkin lebih baik menggunakan clone () dalam banyak kasus:

int[] src = ...
int[] dest = src.clone();
Bala R
sumber
9
+1 untuk tidak mengaktifkan kembali roda. Dan sejauh yang saya tahu, solusi ini lebih cepat Anda bisa dapatkan dalam menyalin array.
Felipe Hummel
6
baik clone dan arraycopy adalah asli. Saya berharap klon menjadi sedikit lebih cepat. Bukan berarti perbedaan itu penting.
MeBigFatGuy
5
@Felipe, @MeBigFatGuy - hanya untuk array besar. Untuk array kecil, copy loop mungkin lebih cepat karena overhead pengaturan. Jika Anda melihat javadoc untuk System.arraycopy, Anda akan melihat bahwa metode ini perlu memeriksa berbagai hal sebelum dimulai. Beberapa pemeriksaan ini tidak perlu dengan loop salinan, tergantung pada jenis array statis.
Stephen C
7
@FelipeHummel, @MeBigFatGuy, @StephenC - Berikut ini adalah tes kinerja metode copy array yang disebutkan dalam jawaban di sini. Dalam pengaturan itu, clone()ternyata menjadi yang tercepat untuk 250.000 elemen.
Adam
6
Sangat mengecewakan untuk melihat bahwa semua diskusi di sini adalah tentang masalah kinerja mikro, yang 99,999% waktu, tidak masalah. Poin yang lebih penting adalah yang src.clone()lebih mudah dibaca dan memiliki jauh lebih sedikit kesempatan untuk kesalahan daripada mengalokasikan array baru dan melakukan arraycopy. (Dan kebetulan juga cepat.)
Brian Goetz
231

kamu bisa menggunakan

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

demikian juga.

MeBigFatGuy
sumber
6
Saya hanya menjelaskan poin OP bahwa: " Jika A berubah menjadi [6,7,8,9,10], B harus tetap [1,2,3,4,5] ". OP mengatakan dia mencoba menggunakan loop tetapi tidak berhasil untuknya.
Harry Joy
15
Para pemain tidak perlu; analisa statis yang baik akan memperingatkan tentang hal itu. Tetapi kloning jelas merupakan cara terbaik untuk membuat salinan array baru.
erickson
5
@MeBigFatGuy - kasus penggunaan OP memerlukan penyalinan berulang ke array yang sama, jadi klon tidak berfungsi.
Stephen C
4
@Stephen C, saya tidak membacanya. Saya baru saja membaca dia ingin salinan, dan kemudian akan berulang kali memperbarui versi yang tidak disimpan.
MeBigFatGuy
4
@ MeBigFatGuy - katanya, "Saya punya array A yang terus diperbarui." . Mungkin saya terlalu banyak membaca tentang itu, tetapi saya menganggap ini menyiratkan bahwa dia berulang kali menyalin A ke B juga.
Stephen C
184

Jika Anda ingin membuat salinan:

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

Inilah cara untuk pergi:

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

Arrays.copyOfmungkin lebih cepat daripada a.clone()pada array kecil. Kedua elemen salin sama-sama cepat tetapi klon () kembali Objectsehingga kompiler harus memasukkan cast implisit ke int[]. Anda bisa melihatnya di bytecode, kira-kira seperti ini:

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

Penjelasan yang bagus dari http://www.journaldev.com/753/how-to-copy-arrays-in-java

Metode Salin Java Array

Object.clone () : Kelas objek menyediakan metode clone () dan karena array di java juga merupakan Object, Anda dapat menggunakan metode ini untuk mencapai salinan array penuh. Metode ini tidak cocok untuk Anda jika Anda ingin menyalin sebagian array.

System.arraycopy () : Sistem kelas arraycopy () adalah cara terbaik untuk melakukan salinan sebagian dari array. Ini memberi Anda cara mudah untuk menentukan jumlah total elemen yang akan disalin dan posisi indeks array sumber dan tujuan. Misalnya System.arraycopy (sumber, 3, tujuan, 2, 5) akan menyalin 5 elemen dari sumber ke tujuan, mulai dari indeks ke-3 sumber ke indeks ke-2 tujuan.

Arrays.copyOf (): Jika Anda ingin menyalin beberapa elemen pertama array atau salinan array penuh, Anda dapat menggunakan metode ini. Jelas itu tidak serbaguna seperti System.arraycopy () tetapi juga tidak membingungkan dan mudah digunakan.

Arrays.copyOfRange () : Jika Anda ingin beberapa elemen array disalin, di mana indeks awal bukan 0, Anda dapat menggunakan metode ini untuk menyalin sebagian array.

Kanagavelu Sugumar
sumber
35

Saya merasa bahwa semua ini "cara yang lebih baik untuk menyalin array" tidak benar-benar akan menyelesaikan masalah Anda.

Kamu bilang

Saya mencoba untuk loop seperti [...] tetapi itu tampaknya tidak berfungsi dengan benar?

Melihat loop itu, tidak ada alasan yang jelas untuk itu tidak berfungsi ... kecuali:

  • Anda entah bagaimana memiliki adan barray kacau (misalnya adan bmerujuk ke array yang sama), atau
  • aplikasi Anda multi-utas dan utas berbeda membaca dan memperbarui aarray secara bersamaan.

Dalam kedua kasus tersebut, cara-cara alternatif melakukan penyalinan tidak akan menyelesaikan masalah yang mendasarinya.

Perbaikan untuk skenario pertama jelas. Untuk skenario kedua, Anda harus mencari cara untuk menyinkronkan utas. Kelas array atom tidak membantu karena mereka tidak memiliki konstruktor salinan atom atau metode kloning, tetapi sinkronisasi menggunakan mutex primitif akan melakukan triknya.

(Ada petunjuk dalam pertanyaan Anda yang membuat saya berpikir bahwa ini memang terkait utas; mis. Pernyataan Anda yang aterus berubah.)

Stephen C
sumber
2
setuju .. mungkin benar.
MeBigFatGuy
17

Anda dapat mencoba menggunakan Arrays.copyOf () di Jawa

int[] a = new int[5]{1,2,3,4,5};
int[] b = Arrays.copyOf(a, a.length);
Durgaprasad Nagarkatte
sumber
3
Redundant: stackoverflow.com/a/15962949/139985 mengatakan hal yang sama.
Stephen C
9

Semua solusi yang memanggil panjang dari array, tambahkan kode Anda redundant null checkersconsider misalnya:

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

Saya sarankan Anda tidak menciptakan roda dan menggunakan kelas utilitas di mana semua pemeriksaan yang diperlukan telah dilakukan. Pertimbangkan ArrayUtils dari apache commons. Kode Anda menjadi lebih pendek:

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

Apache commons dapat Anda temukan di sana

ceri
sumber
8

Anda juga bisa menggunakan Arrays.copyOfRange.

Contoh :

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]
}

Metode ini mirip dengan Arrays.copyOf, tetapi lebih fleksibel. Keduanya digunakan di System.arraycopybawah tenda.

Lihat :

ROMANIA_engineer
sumber
3

Untuk salinan array yang aman dari nol, Anda juga dapat menggunakan opsional dengan Object.clone()metode yang disediakan dalam jawaban ini .

int[] arrayToCopy = {1, 2, 3};
int[] copiedArray = Optional.ofNullable(arrayToCopy).map(int[]::clone).orElse(null);
Nicolas Henneaux
sumber
Terlepas dari kenyataan bahwa solusi ini terlalu rumit, ia juga menyebabkan pemborosan memori dan jika array mengandung rahasia (mis. Byte array dengan kata sandi) itu juga memperkenalkan kelemahan keamanan karena objek perantara akan berada di heap hingga pengumpulan sampah dapat terpapar. untuk penyerang.
Weltraumschaf
1
Saya tidak setuju array akan berada di heap khusus untuk konstruk ini. Memang ia memanggil clone hanya ketika dibutuhkan dan Optionalobjek tersebut hanyalah objek kosong dengan referensi ke array yang ada. Tentang dampak kinerja, saya akan mengatakan itu terlalu dini untuk mengatakan itu sebenarnya dampak karena jenis konstruksi ini adalah kandidat yang baik untuk melakukan inferensi di dalam JVM dan kemudian tidak lebih berdampak daripada metode lain. Ini masalah gaya (pemrograman fungsional versus pemrograman prosedural tetapi tidak hanya) untuk menganggapnya lebih rumit atau tidak.
Nicolas Henneaux
3

Jika Anda harus bekerja dengan array mentah dan tidak ArrayListkemudian Arraysmemiliki apa yang Anda butuhkan. Jika Anda melihat kode sumbernya, ini adalah cara terbaik untuk mendapatkan salinan array. Mereka memiliki sedikit pemrograman defensif karena System.arraycopy()metode ini melempar banyak pengecualian yang tidak dicentang jika Anda memberinya parameter tidak logis.

Anda dapat menggunakan salah satu Arrays.copyOf()yang akan menyalin dari Nthelemen pertama ke array yang lebih pendek baru.

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

Menyalin array yang ditentukan, memotong atau mengisi dengan nol (jika perlu) sehingga salinan memiliki panjang yang ditentukan. Untuk semua indeks yang valid dalam array asli dan salinan, kedua array akan berisi nilai yang identik. Untuk setiap indeks yang valid dalam salinan tetapi bukan yang asli, salinan akan berisi nol. Indeks tersebut akan ada jika dan hanya jika panjang yang ditentukan lebih besar dari pada array asli. Array yang dihasilkan memiliki kelas yang sama persis dengan array asli.

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    }

atau Arrays.copyOfRange()akan melakukan trik:

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

Menyalin kisaran yang ditentukan dari array yang ditentukan ke dalam array baru. Indeks awal rentang (dari) harus berada di antara nol dan asli. Panjang, inklusif. Nilai asli [dari] ditempatkan ke dalam elemen awal dari salinan (kecuali dari == original.length atau dari == ke). Nilai dari elemen berikutnya dalam array asli ditempatkan ke elemen berikutnya dalam salinan. Indeks akhir rentang (ke), yang harus lebih besar dari atau sama dengan dari, mungkin lebih besar dari aslinya. Panjangnya, dalam hal ini nol ditempatkan di semua elemen salinan yang indeksnya lebih besar atau sama dengan aslinya. panjang - dari. Panjang array yang dikembalikan akan ke - dari. Array yang dihasilkan memiliki kelas yang sama persis dengan array asli.

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    }

Seperti yang Anda lihat, keduanya hanyalah fungsi pembungkus System.arraycopydengan logika defensif bahwa apa yang Anda coba lakukan valid.

System.arraycopy adalah cara tercepat mutlak untuk menyalin array.


sumber
0

Saya punya masalah yang sama dengan array 2D dan berakhir di sini. Saya menyalin array utama dan mengubah nilai-nilai array internal, dan terkejut ketika nilai-nilai berubah di kedua salinan. Pada dasarnya kedua salinan itu independen tetapi berisi referensi ke array dalam yang sama dan saya harus membuat array salinan array dalam untuk mendapatkan apa yang saya inginkan.

Ini mungkin bukan masalah OP, tapi saya harap itu masih bisa membantu.

Harpistry
sumber