clone (): ArrayList.clone () я думал, что делает мелкую копию - PullRequest
20 голосов
/ 04 января 2011
ArrayList<Integer> a=new ArrayList<Integer>();
a.add(5);
ArrayList<Integer> b=(ArrayList<Integer>)a.clone();
a.add(6);
System.out.println(b.toString());

В приведенном выше фрагменте кода я думаю, clone() делает мелкую копию. Таким образом, b и a должны указывать на одну и ту же область памяти. Однако, когда я делаю b.toString(), ответ только 5. Почему 6 также не отображается, если clone() делает мелкую копию?

Ответы [ 6 ]

49 голосов
/ 04 января 2011

Мелкая копия не означает, что они указывают на одну и ту же область памяти.Это было бы просто заданием: List b = a;.

При клонировании создается новый экземпляр, содержащий одинаковых элементов.Это означает, что у вас есть 2 разных списка, но их содержимое одинаково.Если вы измените состояние объекта в первом списке, он изменится во втором списке.(Поскольку вы используете неизменяемый тип - Integer - вы не можете это наблюдать)

Однако вы должны рассмотреть возможность не использовать clone().Он отлично работает с коллекциями, но обычно считается сломанным.Используйте копи-конструкторы - new ArrayList(originalList)

6 голосов
/ 04 января 2011

Если бы было , как вы думали, то clone метод будет совершенно бесполезным, потому что в этом случае следующие строки будут эквивалентны:

ArrayList<Integer> b = (ArrayList<Integer>)a.clone();
ArrayList<Integer> b = a;

Клонирование - это, как в сценариях реального мира, процесс создания двух сущностей с одинаковыми свойствами (во время операции клонирования).

И, как упоминал Божоизбегать концепции Java clone().Даже его автор упомянул, что он сломан.

Этот вопрос и ответы на него весьма ценны и дают ссылку на собственные комментарии Джоша Блоха относительно его работы; -)

1 голос
/ 11 января 2017

Shallow Cloning - это стратегия клонирования по умолчанию, предоставляемая Object.clone(), о которой вы говорите. Метод clone() класса объекта создает новый экземпляр и копирует все поля объекта Cloneable в этот новый экземпляр (будь то примитив или ссылка). Таким образом, в случае ссылочных типов в новый экземпляр копируются только ссылочные биты, поэтому ссылочная переменная обоих объектов будет указывать на один и тот же объект. Пример, который мы видели выше, является примером мелкого клонирования.

Глубокое клонирование Как следует из названия, глубокое клонирование означает клонирование всего, от одного объекта до другого. Чтобы достичь этого, нам нужно обмануть наш метод clone(), чтобы обеспечить собственную стратегию клонирования. Мы можем сделать это путем реализации интерфейса Cloneable и переопределения метода clone () в каждом ссылочном типе, который есть в нашей иерархии объектов, а затем вызовем super.clone() и эти методы clone() в методе клона нашего объекта.

Но если вы посмотрите на метод clone () ArrayList в исходном коде, вы увидите, что он внешне копирует v.elementData = Arrays.copyOf(elementData, size); после вызова super.clone(), что означает, что clone () ArrayList глубоко копирует его содержимое

public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

Чтобы узнать больше о клонировании и его типах, таких как глубокое клонирование и мелкое клонирование, прочитайте Java-клонирование и типы клонирования (мелкое и глубокое) в деталях с примером

1 голос
/ 02 октября 2014

Это действительно мелкая копия, вот комментарий для клона из исходного кода ArrayList

Возвращает поверхностную копию этого экземпляра ArrayList.(Сами элементы не копируются.)

Чтобы понять это, давайте посмотрим на фрагмент в методе клонирования из ArrayList

v.elementData = Arrays.copyOf(elementData, size);

Как мы знаем, когда мы назначаем объектв переменную JAVA не делает совершенно новую копию этого объекта.Вместо этого эта переменная становится другой ссылкой, указывающей на исходный объект.

Таким образом, elementData фактически хранит ссылку на объекты, помещенные в этот ArrayList.И клонировать просто скопировать эти ссылки, реплики объектов не создаются.

Конечно, вы можете удалить или добавить новую ссылку на клонированный ArrayList.

Однако, модификация старых объектов в одном ArrayListбудет влиять на оригинальный ArrayList.С вашим примером сложно сделать иллюстрацию, поскольку Integer неизменен.

Чтобы увидеть побочный эффект, вы можете определить пользовательский изменяемый объект

class Person {
        private int a;

        public void setA(int a) {
            this.a = a;
        }
        public int getA() {
            return a;
        }
        @Override
        public String toString() {
            return String.valueOf(a);
        } 
   } 

Затем вы можете использовать следующий код для проверки

        Person p1 = new Person();
        Person p2 = new Person();

        ArrayList<Person> tt = new ArrayList<Person>();
        tt.add(p1);
        tt.add(p2);

        ArrayList<Person> yy = (ArrayList<Person>) tt.clone();
        Person vv = yy.get(yy.indexOf(p2));
        vv.setA(12);
        yy.remove(p1);

        System.out.println("tt: " + tt);
        System.out.println("yy: " +yy);

Вывод долженбыть

tt: [0, 12]
гг: [12]

Смотрите побочный эффект :)?Мы изменяем только элемент в yy, но это также отражается в tt.

0 голосов
/ 10 мая 2014

функция клонирования в Arraylist отличается от копирования одного массива в другой. Если мы используем clone (), он содержит копию исходного массива, но если мы сделаем какие-либо изменения в исходном массиве после использования clone (), он будетне влияет на скопированный массив. Например:

public static void main(String[] a) {

List list = new ArrayList();

list.add("A");

List list2 = ((List) ((ArrayList) list).clone());

System.out.println(list);
System.out.println(list2);

list.clear();

System.out.println(list);
System.out.println(list2);
}

Вывод: -

[A]

[A]

[]

[A]

0 голосов
/ 07 марта 2013

мы не можем динамически выбирать, в какую позицию мы хотим добавить строку, подобную этой

int r=k.nextInt();
Integer i6=new Integer(r);
System.out.println("Enter the address");
String p6=k.nextLine();
ar3.add(i6,p6);

ее нельзя исключить после чтения целого числа

...