Erstellen Sie eine Kopie eines Arrays


345

Ich habe ein Array, adas ständig aktualisiert wird. Sagen wir mal a = [1,2,3,4,5]. Ich muss eine exakte Kopie davon erstellen aund sie aufrufen b. Wenn asich ändern sollte [6,7,8,9,10], bsollte es immer noch sein [1,2,3,4,5]. Was ist der beste Weg, dies zu tun? Ich habe versucht, eine forSchleife wie:

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

aber das scheint nicht richtig zu funktionieren. Bitte verwenden Sie keine fortgeschrittenen Begriffe wie Deep Copy usw., da ich nicht weiß, was das bedeutet.

Antworten:


558

Sie können versuchen, System.arraycopy () zu verwenden

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

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

In den meisten Fällen ist es jedoch wahrscheinlich besser, clone () zu verwenden:

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

9
+1 für nicht wiederbelebtes Rad. Und soweit ich weiß, ist diese Lösung umso schneller, als Sie das Array kopieren können.
Felipe Hummel

6
Sowohl Klon als auch Arraycopy sind nativ. Ich würde erwarten, dass der Klon geringfügig schneller ist. nicht, dass der Unterschied wichtig wäre.
MeBigFatGuy

5
@Felipe, @MeBigFatGuy - nur für ein großes Array. Bei einem kleinen Array kann eine Kopierschleife aufgrund des Einrichtungsaufwands schneller sein. Wenn Sie sich das Javadoc ansehen System.arraycopy, werden Sie feststellen , dass die Methode verschiedene Dinge überprüfen muss, bevor sie startet. Einige dieser Überprüfungen sind bei einer Kopierschleife abhängig von den statischen Array-Typen nicht erforderlich.
Stephen C

7
@FelipeHummel, @MeBigFatGuy, @StephenC - Hier ist ein Leistungstest der in den Antworten hier genannten Array- Kopiermethoden . In dieser Konfiguration clone()erweist es sich als die schnellste für 250 000 Elemente.
Adam

6
Es ist enttäuschend zu sehen, dass sich die gesamte Diskussion hier um Probleme mit der Mikroleistung dreht, die in 99,999% der Fälle keine Rolle spielen. Der wichtigere Punkt ist, dass er src.clone()besser lesbar ist und weitaus weniger Fehlerquellen bietet als das Zuweisen eines neuen Arrays und das Ausführen arraycopy. (Und ist auch schnell.)
Brian Goetz

231

Sie können verwenden

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

auch.


6
Ich kläre nur den Punkt des OP: " Wenn A zu [6,7,8,9,10] wechseln sollte, sollte B immer noch [1,2,3,4,5] sein. " OP sagte, er habe versucht, Loop zu verwenden, aber es habe bei ihm nicht funktioniert.
Harry Joy

15
Die Besetzung ist unnötig; Ein guter statischer Analysator warnt davor. Das Klonen ist jedoch definitiv der beste Weg, um eine neue Kopie eines Arrays zu erstellen.
Erickson

5
@MeBigFatGuy - Der Anwendungsfall des OP beinhaltet das wiederholte Kopieren in dasselbe Array, sodass der Klon nicht funktioniert.
Stephen C

4
@ Stephen C, das habe ich nicht gelesen. Ich habe gerade gelesen, dass er eine Kopie haben möchte, und wird anschließend die nicht versteckte Version wiederholt aktualisieren.
MeBigFatGuy

4
@ MeBigFatGuy - er sagte "Ich habe ein Array A, das ständig aktualisiert wird." . Vielleicht lese ich zu viel darüber, aber ich nehme an, dass er wiederholt auch A nach B kopiert.
Stephen C

184

Wenn Sie eine Kopie erstellen möchten von:

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

Dies ist der richtige Weg:

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

Arrays.copyOfkann schneller sein als a.clone()auf kleinen Arrays. Beide Kopierelemente sind gleich schnell, aber clone () gibt zurück, Objectsodass der Compiler eine implizite Umwandlung in einfügen muss int[]. Sie können es im Bytecode sehen, ungefähr so:

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

62

Schöne Erklärung von http://www.journaldev.com/753/how-to-copy-arrays-in-java

Java-Array-Kopiermethoden

Object.clone () : Die stellt die clone () -Methode bereit. Da das Array in Java auch ein Objekt ist, können Sie diese Methode verwenden, um eine vollständige Array-Kopie zu erzielen. Diese Methode eignet sich nicht für Sie, wenn Sie eine teilweise Kopie des Arrays wünschen.

System.arraycopy () : Die Systemklasse arraycopy () ist der beste Weg, um eine Teilkopie eines Arrays zu erstellen. Auf diese Weise können Sie auf einfache Weise die Gesamtzahl der zu kopierenden Elemente sowie die Indexpositionen des Quell- und Zielarrays angeben. Beispielsweise kopiert System.arraycopy (Quelle, 3, Ziel, 2, 5) 5 Elemente von Quelle zu Ziel, beginnend mit dem 3. Quellindex bis zum 2. Zielindex.

Arrays.copyOf (): Wenn Sie die ersten Elemente eines Arrays oder eine vollständige Kopie des Arrays kopieren möchten, können Sie diese Methode verwenden. Natürlich ist es nicht so vielseitig wie System.arraycopy (), aber es ist auch nicht verwirrend und einfach zu bedienen.

Arrays.copyOfRange () : Wenn Sie möchten, dass nur wenige Elemente eines Arrays kopiert werden, wobei der Startindex nicht 0 ist, können Sie mit dieser Methode ein Teilarray kopieren.


35

Ich habe das Gefühl, dass all diese "besseren Möglichkeiten zum Kopieren eines Arrays" Ihr Problem nicht wirklich lösen werden.

Du sagst

Ich habe eine for-Schleife wie [...] ausprobiert, aber das scheint nicht richtig zu funktionieren?

Wenn man sich diese Schleife ansieht, gibt es keinen offensichtlichen Grund dafür, dass sie nicht funktioniert ... es sei denn:

  • Sie haben die aund bArrays irgendwie durcheinander gebracht (z. B. aund bbeziehen sich auf dasselbe Array), oder
  • Ihre Anwendung ist multithreaded und verschiedene Threads lesen und aktualisieren das aArray gleichzeitig.

In beiden Fällen lösen alternative Methoden zum Kopieren das zugrunde liegende Problem nicht.

Die Lösung für das erste Szenario liegt auf der Hand. Für das zweite Szenario müssen Sie eine Möglichkeit zum Synchronisieren der Threads finden. Atomic Array-Klassen helfen nicht, da sie keine Atomic Copy-Konstruktoren oder Klonmethoden haben, aber die Synchronisierung mit einem primitiven Mutex reicht aus.

(Ihre Frage enthält Hinweise, die mich zu der Annahme veranlassen, dass dies tatsächlich mit dem Thread zusammenhängt, z. B. Ihre Aussage, die asich ständig ändert.)


2
stimmte zu .. wahrscheinlich wahr.
MeBigFatGuy


9

Alle Lösungen, die die Länge des Arrays aufrufen, fügen Sie Ihren Code hinzu.

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

Ich empfehle Ihnen, das Rad nicht zu erfinden und die Utility-Klasse zu verwenden, in der bereits alle erforderlichen Überprüfungen durchgeführt wurden. Betrachten Sie ArrayUtils von Apache Commons. Ihr Code wird kürzer:

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

Apache Commons finden Sie dort


8

Sie können auch verwenden Arrays.copyOfRange.

Beispiel :

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

Diese Methode ähnelt, Arrays.copyOfist jedoch flexibler. Beide verwenden System.arraycopyunter der Haube.

Siehe :


3

Für eine nullsichere Kopie eines Arrays können Sie auch eine Option mit der Object.clone()in dieser Antwort angegebenen Methode verwenden .

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

Trotz der Tatsache, dass diese Lösung zu kompliziert ist, führt sie auch zu Speicherverschwendung. Wenn das Array ein Geheimnis enthält (z. B. ein Byte-Array mit Kennwort), führt dies auch zu Sicherheitslücken, da sich die Zwischenobjekte auf dem Heap befinden, bis die Speicherbereinigung aufgedeckt wird an Angreifer.
Weltraumschaf

1
Ich bin nicht damit einverstanden, dass sich das Array speziell für dieses Konstrukt auf dem Heap befindet. Tatsächlich wird Klon nur bei Bedarf aufgerufen, und das OptionalObjekt ist nur ein leeres Objekt mit einem Verweis auf das vorhandene Array. In Bezug auf die Auswirkungen auf die Leistung würde ich sagen, dass es verfrüht ist zu sagen, dass es sich tatsächlich um eine Auswirkung handelt, da diese Art von Konstrukt ein guter Kandidat für das Inlining innerhalb der JVM ist und dann nicht mehr Auswirkungen als andere Methoden hat. Es ist eine Frage des Stils (funktionale Programmierung versus prozedurale Programmierung, aber nicht nur), sie als komplizierter zu betrachten oder nicht.
Nicolas Henneaux

3

Wenn Sie mit Raw-Arrays arbeiten müssen und nicht, ArrayListdann Arrayshaben Sie das, was Sie brauchen. Wenn Sie sich den Quellcode ansehen, sind dies die absolut besten Möglichkeiten, um eine Kopie eines Arrays zu erhalten. Sie haben ein gutes Stück defensiver Programmierung, da die System.arraycopy()Methode viele ungeprüfte Ausnahmen auslöst, wenn Sie unlogische Parameter eingeben.

Sie können entweder verwenden Arrays.copyOf(), um vom ersten zum NthElement in das neue kürzere Array zu kopieren .

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

Kopiert das angegebene Array, schneidet es ab oder füllt es mit Nullen (falls erforderlich), damit die Kopie die angegebene Länge hat. Für alle Indizes, die sowohl im ursprünglichen Array als auch in der Kopie gültig sind, enthalten die beiden Arrays identische Werte. Für alle Indizes, die in der Kopie gültig sind, jedoch nicht im Original, enthält die Kopie null. Solche Indizes existieren nur dann, wenn die angegebene Länge größer als die des ursprünglichen Arrays ist. Das resultierende Array gehört genau zur selben Klasse wie das ursprüngliche Array.

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    }

oder Arrays.copyOfRange()wird auch den Trick machen:

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

Kopiert den angegebenen Bereich des angegebenen Arrays in ein neues Array. Der Anfangsindex des Bereichs (von) muss zwischen Null und Originallänge einschließlich liegen. Der Wert am Original [von] wird in das Anfangselement der Kopie eingefügt (außer von == original.length oder von == bis). Werte von nachfolgenden Elementen im ursprünglichen Array werden in nachfolgenden Elementen in der Kopie platziert. Der endgültige Index des Bereichs (bis), der größer oder gleich von sein muss, kann größer als original.length sein. In diesem Fall wird in allen Elementen der Kopie, deren Index größer oder gleich original ist, null gesetzt. Länge - von. Die Länge des zurückgegebenen Arrays beträgt to - from. Das resultierende Array gehört genau zur selben Klasse wie das ursprüngliche Array.

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    }

Wie Sie sehen können, sind beide nur Wrapper-Funktionen System.arraycopymit defensiver Logik, dass das, was Sie versuchen, gültig ist.

System.arraycopy ist der absolut schnellste Weg, um Arrays zu kopieren.


0

Ich hatte ein ähnliches Problem mit 2D-Arrays und endete hier. Ich habe das Hauptarray kopiert und die Werte der inneren Arrays geändert und war überrascht, als sich die Werte in beiden Kopien geändert haben. Grundsätzlich waren beide Kopien unabhängig, enthielten jedoch Verweise auf dieselben inneren Arrays, und ich musste eine Reihe von Kopien der inneren Arrays erstellen, um das zu erhalten, was ich wollte.

Es ist wahrscheinlich nicht das Problem des OP, aber ich hoffe, es kann immer noch hilfreich sein.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.