it-swarm.com.ru

Глубокая копия массива объектов

Я хочу сделать глубокую копию массива объектов с помощью конструктора.

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

Однако то, что я имею выше, почему-то не работает. У меня есть автоматизированные тесты, которые я запускаю, и они не проходят эти тесты. Так что здесь есть ошибка, что я не уверен, что это такое.

12
Snowman

То, что вы реализовали, это мелкий копия. Чтобы реализовать deep copy, вы должны Изменить

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

к какой-то вещи, которая назначает copy of other.data[i] для data[i]. Как вы это сделаете, зависит от класса Position. Возможные альтернативы:

  • конструктор копирования: 

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

  • заводской метод: 

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

  • клон: 

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

Заметки:

  1. Выше предполагается, что конструктор копирования, метод фабрики и метод клона соответственно реализуют «правильный» тип копирования, в зависимости от класса Position; увидеть ниже.
  2. Подход clone будет работать только в том случае, если Position явно его поддерживает, и это обычно рассматривается как неполноценное решение. Кроме того, вы должны знать, что нативная реализация clone (то есть метод Object.clone()) делает поверхностную копию.

На самом деле общая проблема реализации глубокого копирования в Java сложна. В случае класса Position можно было бы предположить, что все атрибуты являются примитивными типами (например, целочисленными или двойными), и поэтому копирование с глубоким или поверхностным копированием является спорным. Но если есть ссылочные атрибуты, то вам нужно полагаться на конструктор копирования/метод фабрики/метод клона, чтобы выполнить тот тип копирования, который вам необходим. В каждом случае его нужно запрограммировать. А в общем случае (где вам приходится иметь дело с циклами) это сложно и требует от каждого класса реализации специальных методов.

Существует еще один потенциальный способ скопировать массив объектов. Если объекты в массиве являются serializable, то вы можете скопировать их, используя ObjectOutputStream и ObjectInputStream serialize, а затем десериализовать массив. Тем не мение:

  • это дорого,
  • это работает, только если объекты (транзитивно) сериализуемы, и
  • значения любых полей transient не будут скопированы.

Копирование по сериализации не рекомендуется. Было бы лучше поддержать клонирование или какой-либо другой метод. 

В общем, глубокое копирование лучше избегать в Java.

Наконец, чтобы ответить на ваш вопрос о работе конструктора копирования классов Position, я ожидаю, что это примерно так:

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

Как говорит @Turtle, в этом нет никакой магии. Вы реализуете конструктор (вручную), который инициализирует его состояние путем копирования из существующего экземпляра.

23
Stephen C

Когда ты сказал:

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

Вы просто копируете список ссылок (предполагая, что это массив объектов). Если вы хотите сделать глубокую копию, вам нужно использовать new для создания нового экземпляра каждого объекта в массиве.

2
Justin Ethier

Вместо того чтобы говорить:

data[i]=other.data[i]

Вы захотите сделать конструктор копирования для Position (другими словами, конструктор для Position, который принимает другую Position и копирует примитивные данные внутри него) и говорит data[i]=new Position(other.data[i]);

По сути, ваш конструктор «глубокого копирования» PositionList является конструктором копирования, хотя конструктор копирования обычно указывает на глубокое копирование, поэтому параметр deepCopy не нужен.

1
Thomas

Вот функция, которую я использую:

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

Работает только с массивами с объектами одного уровня.

0
Kainan