Fai una copia di un array


345

Ho un array ache viene costantemente aggiornato. Diciamo di Let a = [1,2,3,4,5]. Devo fare una copia duplicata esatta ae chiamarla b. Se adovesse cambiare [6,7,8,9,10], bdovrebbe essere ancora [1,2,3,4,5]. Qual è il modo migliore per farlo? Ho provato un forciclo come:

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

ma questo non sembra funzionare correttamente. Per favore, non usare termini avanzati come copia profonda, ecc., Perché non so cosa significhi.

Risposte:


558

Puoi provare a utilizzare System.arraycopy ()

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

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

Ma, probabilmente, è meglio usare clone () nella maggior parte dei casi:

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

9
+1 per non reinventare la ruota. E per quanto ne so, questa soluzione è la più veloce che puoi ottenere nella copia di array.
Felipe Hummel,

6
sia il clone che l'arraycopy sono nativi. Mi aspetto che il clone sia leggermente più veloce. non che la differenza sia importante.
MeBigFatGuy

5
@Felipe, @MeBigFatGuy - solo per un array di grandi dimensioni. Per un array di piccole dimensioni, un ciclo di copia potrebbe essere più veloce a causa delle spese generali di installazione. Se guardi javadoc per System.arraycopy, vedrai che il metodo deve controllare varie cose prima che inizi. Alcuni di questi controlli non sono necessari con un ciclo di copia, a seconda dei tipi di array statici.
Stephen C,

7
@FelipeHummel, @MeBigFatGuy, @StephenC - Ecco un test delle prestazioni dei metodi di copia dell'array menzionati nelle risposte qui. In tale configurazione, clone()risulta essere il più veloce per 250.000 elementi.
Adam

6
È deludente vedere che tutta la discussione qui riguarda problemi di micro-prestazioni, che il 99,999% delle volte, non importa. Il punto più importante è che src.clone()è più leggibile e ha molte meno possibilità di errore rispetto all'allocazione di un nuovo array e al fare arraycopy. (E sembra anche essere veloce.)
Brian Goetz,

231

Puoi usare

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

anche.


6
Sto solo chiarendo il punto del PO che: " Se A dovesse cambiare in [6,7,8,9,10], B dovrebbe comunque essere [1,2,3,4,5] ". OP ha detto che ha provato a usare loop ma non ha funzionato per lui.
Harry Joy,

15
Il cast non è necessario; un buon analizzatore statico lo avvertirà. Ma la clonazione è sicuramente il modo migliore per creare una nuova copia di un array.
Erickson,

5
@MeBigFatGuy - il caso d'uso dell'OP comporta una ripetuta copia nello stesso array, quindi il clone non funziona.
Stephen C,

4
@Stephen C, non l'ho letto. Ho appena letto che ne vuole una copia e successivamente aggiornerò ripetutamente la versione non nascosta.
MeBigFatGuy

4
@MeBigFatGuy - ha detto "Ho un array A che viene costantemente aggiornato." . Forse sto leggendo troppo in quello, ma prendo questo implicando che sta ripetutamente copiando anche A in B.
Stephen C,

184

Se vuoi fare una copia di:

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

Questa è la strada da percorrere:

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

Arrays.copyOfpotrebbe essere più veloce rispetto a.clone()ai piccoli array. Entrambi gli elementi della copia sono ugualmente veloci ma clone () restituisce Objectquindi il compilatore deve inserire un cast implicito int[]. Puoi vederlo nel bytecode, qualcosa del genere:

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

62

Bella spiegazione da http://www.journaldev.com/753/how-to-copy-arrays-in-java

Metodi di copia dell'array Java

Object.clone () : la classe Object fornisce il metodo clone () e poiché l'array in java è anche un oggetto, è possibile utilizzare questo metodo per ottenere una copia completa dell'array. Questo metodo non è adatto se si desidera una copia parziale dell'array.

System.arraycopy () : la classe di sistema arraycopy () è il modo migliore per eseguire una copia parziale di un array. Fornisce un modo semplice per specificare il numero totale di elementi da copiare e le posizioni dell'indice dell'array di origine e destinazione. Ad esempio System.arraycopy (sorgente, 3, destinazione, 2, 5) copierà 5 elementi dall'origine alla destinazione, a partire dal 3 ° indice dell'origine al 2 ° indice della destinazione.

Arrays.copyOf (): se si desidera copiare i primi elementi di un array o la copia completa dell'array, è possibile utilizzare questo metodo. Ovviamente non è versatile come System.arraycopy () ma non è nemmeno confuso e facile da usare.

Arrays.copyOfRange () : se si desidera copiare pochi elementi di un array, dove l'indice iniziale non è 0, è possibile utilizzare questo metodo per copiare un array parziale.


35

Ho la sensazione che tutti questi "modi migliori per copiare un array" non risolveranno davvero il tuo problema.

Tu dici

Ho provato un ciclo for come [...] ma non sembra funzionare correttamente?

Guardando quel ciclo, non c'è motivo ovvio per cui non funzioni ... a meno che:

  • hai in qualche modo incastrato gli array ae b(es. ae fai briferimento allo stesso array), oppure
  • l'applicazione è multi-thread e diversi thread stanno leggendo e aggiornando l' aarray contemporaneamente.

In entrambi i casi, modi alternativi di eseguire la copia non risolveranno il problema sottostante.

La correzione per il primo scenario è ovvia. Per il secondo scenario dovrai trovare un modo per sincronizzare i thread. Le classi di array atomici non aiutano perché non hanno costruttori di copie atomiche o metodi di clonazione, ma la sincronizzazione tramite un mutex primitivo farà il trucco.

(Ci sono suggerimenti nella tua domanda che mi portano a pensare che questo sia effettivamente legato al thread; ad esempio la tua affermazione che aè in continua evoluzione.)


2
d'accordo .. probabilmente vero.
MeBigFatGuy


9

Tutte le soluzioni che chiamano lunghezza dall'array, aggiungono il tuo esempio di checkers null ridondante di codice:

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

Ti consiglio di non inventare la ruota e di utilizzare la classe di utilità in cui sono già stati eseguiti tutti i controlli necessari. Considera ArrayUtils dai comuni di apache. Il codice diventa più breve:

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

Comuni Apache che puoi trovare


8

Puoi anche usare Arrays.copyOfRange.

Esempio :

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

Questo metodo è simile Arrays.copyOf, ma è più flessibile. Entrambi usano System.arraycopysotto il cofano.

Vedi :


3

Per una copia null-safe di un array, è anche possibile utilizzare un facoltativo con il Object.clone()metodo fornito in questa risposta .

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

Nonostante il fatto che questa soluzione sia troppo complicata, introduce anche spreco di memoria e se l'array contiene un segreto (ad es. Array di byte con password) introduce anche difetti di sicurezza perché gli oggetti intermedi risiederanno sull'heap fino a quando la raccolta dei rifiuti potrebbe essere esposta agli attaccanti.
Weltraumschaf,

1
Non sono d'accordo che l'array sarà sull'heap appositamente per questo costrutto. In effetti chiama clone solo quando necessario e l' Optionaloggetto è solo un oggetto vuoto con un riferimento all'array esistente. Per quanto riguarda l'impatto sulle prestazioni, direi che è prematuro affermare che in realtà è un impatto poiché questo tipo di costrutto è un buon candidato per l'inserimento all'interno della JVM e quindi non più impatto rispetto ad altri metodi. È una questione di stile (programmazione funzionale contro programmazione procedurale, ma non solo) considerarlo più complicato o meno.
Nicolas Henneaux,

3

Se si deve lavorare con array prime e non ArrayListquindi Arraysha quello che serve. Se guardi il codice sorgente, questi sono i modi assolutamente migliori per ottenere una copia di un array. Hanno un bel po 'di programmazione difensiva perché il System.arraycopy()metodo genera molte eccezioni non controllate se si alimentano parametri illogici.

È possibile utilizzare uno dei due Arrays.copyOf()che verrà copiato dal primo Nthall'elemento nel nuovo array più corto.

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

Copia l'array specificato, troncando o riempiendo con null (se necessario) in modo che la copia abbia la lunghezza specificata. Per tutti gli indici validi sia nell'array originale che nella copia, i due array conterranno valori identici. Per tutti gli indici validi nella copia ma non l'originale, la copia conterrà null. Tali indici esistono se e solo se la lunghezza specificata è maggiore di quella dell'array originale. L'array risultante è esattamente della stessa classe dell'array originale.

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    }

o Arrays.copyOfRange()farà anche il trucco:

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

Copia l'intervallo specificato dell'array specificato in un nuovo array. L'indice iniziale dell'intervallo (da) deve trovarsi tra zero e lunghezza originale, incluso. Il valore nell'originale [da] viene inserito nell'elemento iniziale della copia (a meno che da == original.length o da == a). I valori degli elementi successivi nell'array originale vengono inseriti negli elementi successivi nella copia. L'indice finale dell'intervallo (a), che deve essere maggiore o uguale a da, può essere maggiore di original.length, nel qual caso il valore null viene inserito in tutti gli elementi della copia il cui indice è maggiore o uguale all'originale. lunghezza - da. La lunghezza dell'array restituito sarà a - da. L'array risultante è esattamente della stessa classe dell'array originale.

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    }

Come puoi vedere, entrambi questi sono solo funzioni wrapper System.arraycopycon logica difensiva che ciò che stai cercando di fare è valido.

System.arraycopy è il modo più veloce in assoluto per copiare array.


0

Ho avuto un problema simile con gli array 2D e sono finito qui. Stavo copiando l'array principale e cambiando i valori degli array interni, e sono rimasto sorpreso quando i valori sono cambiati in entrambe le copie. Fondamentalmente entrambe le copie erano indipendenti ma contenevano riferimenti agli stessi array interni e ho dovuto realizzare una serie di copie degli array interni per ottenere ciò che volevo.

Probabilmente non è un problema del PO, ma spero che possa ancora essere utile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.