web-dev-qa-db-fra.com

Copie profonde d'un tableau d'objets

Je veux faire une copie complète d'un tableau d'objets en utilisant un constructeur.

public class PositionList {
    private Position[] data = new Position[0];

public PositionList(PositionList other, boolean deepCopy) {
        if (deepCopy){
            size=other.getSize();
            data=new Position[other.data.length];
            for (int i=0;i<data.length;i++){
            data[i]=other.data[i];
            }

Cependant, ce que j'ai ci-dessus pour une raison quelconque ne fonctionne pas. J'ai des tests automatisés que je lance et ses tests échouent. Donc, il y a une erreur ici et je ne suis pas sûr de ce que c'est.

12
Snowman

Ce que vous avez implémenté est une copie peu profonde. Pour implémenter une copie profonde, vous devez Changer

data[i] = other.data[i];

à quelque chose qui assigne un copie de other.data[i] à data[i]. Cela dépend de la classe Position. Les alternatives possibles sont:

  • un constructeur de copie: 

    data[i] = new Position(other.data[i]);

  • une méthode d'usine: 

    data[i] = createPosition(other.data[i]);

  • cloner: 

    data[i] = (Position) other.data[i].clone();

Remarques:

  1. Les opérations ci-dessus supposent que le constructeur de copie, la méthode d'usine et la méthode de clonage implémentent respectivement le type de copie "correct", en fonction de la classe Position; voir ci-dessous.
  2. L'approche clone ne fonctionnera que si Position la prend explicitement en charge, ce qui est généralement considéré comme une solution inférieure. De plus, vous devez savoir que l'implémentation native de clone (c'est-à-dire la méthode Object.clone()) effectue une copie superficielle.

En fait, le problème général de la mise en œuvre de la copie en profondeur en Java est compliqué. Dans le cas de la classe Position, on pourrait supposer que les attributs sont tous des types primitifs (par exemple, ints ou doubles) et qu’une copie profonde par opposition à une copie superficielle est donc sans objet. Mais s'il existe des attributs de référence, vous devez vous appuyer sur le constructeur de copie/la méthode d'usine/la méthode de clonage pour effectuer le type de copie dont vous avez besoin. Dans chaque cas, il doit être programmé. Et dans le cas général (où vous devez traiter des cycles), il est difficile et nécessite que chaque classe mette en œuvre des méthodes spéciales.

Il existe un autre moyen potentiel pour copier un tableau d'objets. Si les objets du tableau sont serializable , vous pouvez les copier en utilisant ObjectOutputStream et ObjectInputStream sérialiser, puis désérialiser le tableau. Toutefois:

  • c'est cher,
  • cela ne fonctionne que si les objets sont sérialisables (de manière transitoire), et
  • les valeurs des champs transient ne seront pas copiées.

La copie par sérialisation n'est pas recommandée. Il serait préférable de soutenir le clonage ou une autre méthode. 

Dans l’ensemble, il est préférable d’éviter la copie en profondeur en Java.

Enfin, pour répondre à votre question sur le constructeur de la copie des classes Position, je suppose que cela ressemble à ceci:

public class Position {
    private int x;
    private int y;
    ...
    public Position(Position other) {
        this.x = other.x;
        this.y = other.y;
    }
    ...
}

Comme @Turtle dit, il n'y a pas de magie impliquée. Vous implémentez un constructeur (à la main) qui initialise son état en copiant à partir d'une instance existante.

23
Stephen C

Quand tu dis:

data[i]=other.data[i];

Vous ne faites que copier une liste de références (en supposant qu'il s'agisse d'un tableau d'objets). Si vous souhaitez effectuer une copie complète, vous devez utiliser new pour créer une nouvelle instance de chaque objet du tableau.

2
Justin Ethier

Au lieu de dire:

data[i]=other.data[i]

Vous voudrez créer un constructeur de copie pour Position (en d’autres termes, un constructeur pour Position prenant une autre Position et copiant les données primitives qu’il contient) et dire data[i]=new Position(other.data[i]);.

Fondamentalement, votre constructeur "copie en profondeur" le PositionList est un constructeur de copie, bien que le constructeur de copie ait tendance à indiquer une copie en profondeur, de sorte que le paramètre deepCopy est inutile.

1
Thomas

Voici une fonction que j'utilise:

function copy(arr) {
  return arr
    .map(x => Object
      .keys(x)
      .reduce((acc, y) => {
        acc[y] = x[y]
        return acc
      }, {}))
}

Cela ne fonctionne que sur les tableaux avec des objets avec un seul niveau.

0
Kainan