【问题标题】:clone(): ArrayList.clone() I thought does a shallow copyclone(): ArrayList.clone() 我以为做了一个浅拷贝
【发布时间】:2011-06-03 07:27:26
【问题描述】:
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() 做了一个浅拷贝。所以,ba 应该指向同一个内存位置。但是,当我做b.toString() 时,答案只有5。如果clone() 进行浅拷贝,为什么6 也不显示?

【问题讨论】:

  • 这里关于浅拷贝的相同查询:我希望它复制了对象类型字段的引用和原始类型字段的值。 stackoverflow.com/a/4592545/7375748 这个答案有帮助,避免克隆()。我现在让它继续,什么时候它会保持与复制时相同的内容,什么时候它会随着源的更新自动更新。

标签: java clone


【解决方案1】:

浅拷贝并不意味着它们指向同一个内存位置。那只是一个任务:List b = a;

克隆会创建一个 实例,其中包含相同的 元素。这意味着您有 2 个不同的列表,但它们的内容是相同的。如果您在第一个列表中更改对象的状态,它将在第二个列表中更改。 (由于您使用的是不可变类型 - Integer - 您无法观察到这一点)

但是,您应该考虑不使用clone()。它适用于集合,但通常被认为已损坏。使用复制构造函数 - new ArrayList(originalList)

【讨论】:

  • 那么深拷贝和浅拷贝有什么区别
  • 是否可以说创建一个只包含可变类对象的列表的克隆是一种浪费。
  • @TimeToCodeTheRoad 浅拷贝就像 Bozho 解释的那样工作,深拷贝复制 ArrayList 及其元素。
  • 只是稍微扩展一下@JoãoPortela 所说的话,深拷贝会导致所有对象都被复制。你有一个新的容器对象,里面的所有对象也都被复制了。但是请注意,这些对象(在集合中)中包含的对象可能不会被复制。您必须确保每个对象都正确地实现了深拷贝。如果操作正确,您可以从 N 个对象变为 2N 个对象。
  • @LeiLeyba 不,这是两个单独的列表,但底层对象将保持不变。这意味着如果我从列表a 中检索一些对象o 并对其进行修改,那么它在所有其他引用它的地方都会发生变化,包括列表b。这并不意味着当我从列表a 中删除对对象o 的引用时,它也会从列表b 中删除。
【解决方案2】:

如果它如你所想,那么clone方法完全没用,因为在这种情况下,以下几行 等价:

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

克隆 - 就像在现实世界的场景中一样 - 创建两个具有完全相同属性的实体的过程(在克隆操作时)。

正如 Bozho 所提到的 - 避免使用 Java clone() 概念。就连作者都提到它坏了。

This question and it's answers 非常有价值,并提供了 Josh Blochs 自己作品中的 cmets 的链接;-)

【讨论】:

    【解决方案3】:

    这确实做了一个浅拷贝,这里是对克隆的注释,来自 ArrayList 源代码

    返回此 ArrayList 实例的浅表副本。 (元素本身不会被复制。)

    为了理解这一点,让我们看一下 ArrayList 中的一个 sn-p in clone 方法

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

    正如我们所知,当我们将一个 Object 赋值给一个变量时,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中。

    【讨论】:

      【解决方案4】:

      浅克隆是你说的Object.clone()提供的默认克隆策略。 对象类的 clone() 方法创建一个新实例并将 Cloneable 对象的所有字段复制到该新实例(它是原始实例或引用)。所以在引用类型的情况下,只有引用位被复制到新实例,因此,两个对象的引用变量将指向同一个对象。我们上面看到的例子就是一个浅克隆的例子。

      深度克隆 顾名思义,深度克隆意味着将所有内容从一个对象克隆到另一个对象。为了实现这一点,我们需要欺骗我们的clone() 方法来提供我们自己的克隆策略。我们可以通过在对象层次结构中的每个引用类型中实现Cloneable 接口和覆盖clone() 方法,然后在对象的clone 方法中调用super.clone() 和这些clone() 方法。

      但是如果你看源码中ArrayList的clone()方法,你会发现它在调用super.clone()之后是在外部复制v.elementData = Arrays.copyOf(elementData, size);,也就是说ArrayList的clone()会深度复制它的内容

      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 Cloning and Types of Cloning (Shallow and Deep) in Details with Example

      【讨论】:

        【解决方案5】:

        我们不能像这样动态选择我们想在什么位置添加字符串

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

        读取整数后不执行

        【讨论】:

          【解决方案6】:

          Arraylist中的clone函数与将一个arraylist复制到另一个不同,如果我们使用clone(),它会保存原始arraylist的副本,但是如果我们在使用clone()之后对原始arraylist进行任何更改,它将不影响复制的arraylist .. 例如:

          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]

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-07-13
            • 2013-03-09
            • 1970-01-01
            • 2012-04-12
            • 1970-01-01
            • 2013-10-04
            • 2015-01-13
            • 2011-09-05
            相关资源
            最近更新 更多