Faire une copie d'un tableau


345

J'ai un tableau aqui est constamment mis à jour. Disons a = [1,2,3,4,5]. Je dois faire une copie exacte de aet l'appeler b. Si aje devais changer [6,7,8,9,10], bje devrais l'être [1,2,3,4,5]. Quelle est la meilleure façon de procéder? J'ai essayé une forboucle comme:

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

mais cela ne semble pas fonctionner correctement. Veuillez ne pas utiliser de termes avancés comme copie profonde, etc., car je ne sais pas ce que cela signifie.

Réponses:


558

Vous pouvez essayer d'utiliser System.arraycopy ()

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

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

Mais, probablement mieux d'utiliser clone () dans la plupart des cas:

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

9
+1 pour ne pas avoir remis la roue. Et pour autant que je le sache, cette solution est la plus rapide que vous pouvez obtenir dans la copie de tableau.
Felipe Hummel

6
clone et arraycopy sont natifs. Je m'attendrais à ce que le clone soit légèrement plus rapide. pas que la différence soit importante.
MeBigFatGuy

5
@Felipe, @MeBigFatGuy - uniquement pour un grand tableau. Pour un petit tableau, une boucle de copie peut être plus rapide en raison des frais généraux de configuration. Si vous regardez le javadoc System.arraycopy, vous verrez que la méthode doit vérifier diverses choses avant de commencer. Certaines de ces vérifications sont inutiles avec une boucle de copie, selon les types de tableaux statiques.
Stephen C

7
@FelipeHummel, @MeBigFatGuy, @StephenC - Voici un test de performance des méthodes de copie de tableau mentionnées dans les réponses ici. Dans cette configuration, clone()se révèle être le plus rapide pour 250 000 éléments.
Adam

6
Il est décevant de voir que toute la discussion ici porte sur les problèmes de micro-performances, ce qui n'a pas d'importance 99,999% du temps. Le point le plus important est qu'il src.clone()est plus lisible et a beaucoup moins de possibilités d'erreur que d'allouer un nouveau tableau et de le faire arraycopy. (Et se trouve également être rapide.)
Brian Goetz

231

vous pouvez utiliser

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

ainsi que.


6
Je suis juste en train de clarifier le point du PO selon lequel: " Si A devait passer à [6,7,8,9,10], B devrait toujours être [1,2,3,4,5] ". OP a dit qu'il avait essayé d'utiliser la boucle mais ne fonctionnait pas pour lui.
Harry Joy

15
Le casting est inutile; un bon analyseur statique vous en avertira. Mais le clonage est certainement le meilleur moyen de faire une nouvelle copie d'un tableau.
erickson

5
@MeBigFatGuy - le cas d'utilisation de l'OP implique une copie répétée vers le même tableau, donc le clone ne fonctionne pas.
Stephen C

4
@Stephen C, je n'ai pas lu ça. Je viens de lire qu'il veut une copie, puis il mettra ensuite à jour à plusieurs reprises la version non cachée.
MeBigFatGuy

4
@MeBigFatGuy - il a dit "J'ai un tableau A qui est constamment mis à jour." . Peut-être que j'en lis trop, mais je considère que cela implique qu'il copie également A à B à plusieurs reprises.
Stephen C

184

Si vous souhaitez faire une copie de:

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

C'est la voie à suivre:

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

Arrays.copyOfpeut être plus rapide que a.clone()sur les petits tableaux. Les deux éléments de copie sont également rapides mais clone () renvoie Objectdonc le compilateur doit insérer un transtypage implicite vers int[]. Vous pouvez le voir dans le bytecode, quelque chose comme ceci:

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

62

Belle explication de http://www.journaldev.com/753/how-to-copy-arrays-in-java

Méthodes de copie de tableau Java

Object.clone () : la classe Object fournit la méthode clone () et puisque le tableau en java est également un objet, vous pouvez utiliser cette méthode pour obtenir une copie complète du tableau. Cette méthode ne vous conviendra pas si vous souhaitez une copie partielle du tableau.

System.arraycopy () : la classe système arraycopy () est la meilleure façon de faire une copie partielle d'un tableau. Il vous permet de spécifier facilement le nombre total d'éléments à copier et les positions d'index du tableau source et de destination. Par exemple, System.arraycopy (source, 3, destination, 2, 5) copiera 5 éléments de la source à la destination, en commençant du 3e index de la source au 2e index de la destination.

Arrays.copyOf (): si vous souhaitez copier les premiers éléments d'un tableau ou une copie complète du tableau, vous pouvez utiliser cette méthode. Évidemment, ce n'est pas polyvalent comme System.arraycopy () mais ce n'est pas non plus déroutant et facile à utiliser.

Arrays.copyOfRange () : Si vous voulez que quelques éléments d'un tableau soient copiés, où l'index de départ n'est pas 0, vous pouvez utiliser cette méthode pour copier un tableau partiel.


35

J'ai le sentiment que toutes ces "meilleures façons de copier un tableau" ne vont pas vraiment résoudre votre problème.

Vous dites

J'ai essayé une boucle for comme [...] mais cela ne semble pas fonctionner correctement?

En regardant cette boucle, il n'y a aucune raison évidente pour qu'elle ne fonctionne pas ... à moins que:

  • vous avez en quelque sorte les tableaux aet bfoirés (par exemple aet faites bréférence au même tableau), ou
  • votre application est multithread et différents threads lisent et mettent à jour le atableau simultanément.

Dans les deux cas, d'autres façons de faire la copie ne résoudront pas le problème sous-jacent.

La solution pour le premier scénario est évidente. Pour le deuxième scénario, vous devrez trouver un moyen de synchroniser les threads. Les classes de tableaux atomiques n'aident pas car elles n'ont pas de constructeurs de copie atomique ou de méthodes de clonage, mais la synchronisation à l'aide d'un mutex primitif fera l'affaire.

(Il y a des indices dans votre question qui m'amènent à penser que c'est effectivement lié au thread; par exemple, votre déclaration qui achange constamment.)


2
d'accord .. probablement vrai.
MeBigFatGuy


9

Toutes les solutions qui appellent la longueur du tableau, ajoutez votre code redondant null checkersconsider exemple:

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

Je vous recommande de ne pas inventer la roue et d'utiliser la classe utilitaire où toutes les vérifications nécessaires ont déjà été effectuées. Considérez ArrayUtils d'apache commons. Votre code devient plus court:

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

Apache commons vous pouvez y trouver


8

Vous pouvez également utiliser Arrays.copyOfRange.

Exemple :

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

Cette méthode est similaire à Arrays.copyOf, mais elle est plus flexible. Les deux utilisent System.arraycopysous le capot.

Voir :


3

Pour une copie null-safe d'un tableau, vous pouvez également utiliser une option avec la Object.clone()méthode fournie dans cette réponse .

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

Malgré le fait que cette solution est trop compliquée, elle introduit également un gaspillage de mémoire et si le tableau contient un secret (par exemple un tableau d'octets avec mot de passe), il introduit également des failles de sécurité car les objets intermédiaires résideront sur le tas jusqu'à ce que la récupération de place puisse être exposée. aux attaquants.
Weltraumschaf

1
Je ne suis pas d'accord que le tableau sera sur le tas spécialement pour cette construction. En effet, il n'appelle clone qu'en cas de besoin et l' Optionalobjet n'est qu'un objet vide avec une référence au tableau existant. En ce qui concerne l'impact sur les performances, je dirais qu'il est prématuré de dire qu'il s'agit en fait d'un impact, car ce type de construction est un bon candidat pour être intégré à la JVM et donc pas plus d'impact que les autres méthodes. C'est une question de style (programmation fonctionnelle versus programmation procédurale mais pas seulement) de la considérer comme plus compliquée ou non.
Nicolas Henneaux

3

Si vous devez travailler avec des tableaux bruts et non ArrayListalors Arraysa ce que vous avez besoin. Si vous regardez le code source, ce sont les meilleures façons d'obtenir une copie d'un tableau. Ils ont une bonne programmation défensive parce que la System.arraycopy()méthode lève beaucoup d'exceptions non contrôlées si vous lui donnez des paramètres illogiques.

Vous pouvez utiliser l'un ou l'autre Arrays.copyOf()qui copiera du premier Nthélément vers le nouveau tableau plus court.

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

Copie le tableau spécifié, en le tronquant ou en le remplissant avec des valeurs nulles (si nécessaire) afin que la copie ait la longueur spécifiée. Pour tous les index valides à la fois dans le tableau d'origine et dans la copie, les deux tableaux contiendront des valeurs identiques. Pour tous les index valides dans la copie mais pas l'original, la copie contiendra null. De tels indices existeront si et seulement si la longueur spécifiée est supérieure à celle du tableau d'origine. Le tableau résultant est exactement de la même classe que le tableau d'origine.

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    }

ou Arrays.copyOfRange()fera aussi l'affaire:

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

Copie la plage spécifiée du tableau spécifié dans un nouveau tableau. L'index initial de la plage (de) doit être compris entre zéro et la longueur d'origine, inclus. La valeur à l'original [de] est placée dans l'élément initial de la copie (sauf de == original.length ou de == to). Les valeurs des éléments suivants du tableau d'origine sont placées dans les éléments suivants de la copie. L'index final de la plage (to), qui doit être supérieur ou égal à from, peut être supérieur à original.length, auquel cas null est placé dans tous les éléments de la copie dont l'index est supérieur ou égal à original. longueur - de. La longueur du tableau retourné sera de - de. Le tableau résultant est exactement de la même classe que le tableau d'origine.

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    }

Comme vous pouvez le voir, ces deux fonctions ne sont que des fonctions d'encapsulation System.arraycopyavec une logique défensive selon laquelle ce que vous essayez de faire est valide.

System.arraycopy est le moyen le plus rapide de copier des tableaux.


0

J'ai eu un problème similaire avec les tableaux 2D et je me suis arrêté ici. Je copiais le tableau principal et modifiais les valeurs des tableaux internes, et j'ai été surpris lorsque les valeurs ont changé dans les deux copies. Fondamentalement, les deux copies étaient indépendantes mais contenaient des références aux mêmes tableaux internes et j'ai dû créer un tableau de copies des tableaux internes pour obtenir ce que je voulais.

Ce n'est probablement pas le problème du PO, mais j'espère qu'il pourra toujours être utile.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.