Java 将ArrayList元素克隆到同一ArrayList

Java 将ArrayList元素克隆到同一ArrayList,java,arraylist,clone,Java,Arraylist,Clone,我有一个包含对象的ArrayList。(这些对象是类别,每个对象都包含难以置信的大量信息,正是这些对象代表了大型发电机,因此不能逐个复制属性) 例如: ArrayList arr = {ob1, ob2, ob3, ob4, ob5} 因此,我尝试将对象(ob1)克隆到位置5 arr.set(4, arr.get(0)); 但不知何故,这样做,ob5不是ob1的副本,它是ob1,所以如果我改变ob5,ob1也会改变 这是ArrayList的固有特性吗? 如果我改用列表,有什么区别吗?这是因为

我有一个包含对象的
ArrayList
。(这些对象是类别,每个对象都包含难以置信的大量信息,正是这些对象代表了大型发电机,因此不能逐个复制属性)

例如:

ArrayList arr = {ob1, ob2, ob3, ob4, ob5}
因此,我尝试将对象(ob1)克隆到位置5

arr.set(4, arr.get(0));
但不知何故,这样做,ob5不是ob1的副本,它是ob1,所以如果我改变ob5,ob1也会改变

这是ArrayList的固有特性吗?
如果我改用列表,有什么区别吗?

这是因为您正在进行卷影复制而不是深度复制

浅层复制:如果在
Obj-1
上执行浅层复制,则会复制该对象,但不会复制其包含的对象。包含的对象
Obj-1
Obj-2
受克隆的Obj-2的更改影响。默认情况下,当类实现Java.lang.Cloneable接口时,Java支持对象的浅层克隆

深度复制:如果对所示的
obj-1
执行深度复制,则不仅
obj-
1已被复制,而且其中包含的对象也已被复制。序列化可用于实现深度克隆。通过序列化进行深度克隆开发更快,维护更容易,但会带来性能开销

深度复制的一个示例:

class Student implements Cloneable {
  //Contained object
  private Subject subj;
  private String name;

  public Subject getSubj() {..}
  public String getName() {..}

  public Object clone() {
    //Deep copy
    Student s = new Student(name, subj.getName());
    return s;
  }
}

在Java的任何地方都是如此。除非您明确地这样做,否则不会复制任何内容。(事实上,有些对象是故意不可能复制的。)

您需要制作一份发电机的副本。最常见的选项是引入复制构造函数或实现克隆方法。但是,这两种解决方案都需要复制所有这些属性

一旦你做到了,你就可以做到

//copy constructor
arr.add( new ElectricGenerator( arr.get( 0 ) ) );
//clone
arr.add( arr.get( 0 ).clone() );

另一种方法是序列化/反序列化对象

这不符合ArrayList,这是java特性。不创建新对象,只传递引用。因此,ArrayList存储两个引用。您必须使用对象的.clone()方法

arr.set(4, arr.get(0).clone());

这将创建对象的新副本,即使此副本很浅。如果需要,您必须重写clone方法以使其成为深度副本。

否,因为在Java中,您创建了对对象的引用,因此即使您不使用ArrayList并执行类似操作:

String a = new String("a");
String b = a;

b += "b";
a和b都等于“ab”

(这里的例外是int之类的基本类型(但不是Integer的实例,有不同的东西))

因此,您的问题的答案如下: 在这里:

列表仅包含指向对象的指针

如果将ob1移动到ob5,会发生什么情况?您会告诉列表将指向ob1的指针放在ob5指针所在的位置,从而丢失指向ob5的指针并复制指向ob1的指针


您需要创建另一个包含重复值的对象,然后将此对象分配到位置4

对象变量不同于原始数据类型

int x = 2;
int y = x;
x和y表示不同的内存位置

MyClass x = new MyClass();
MyClass y = x;

x和y引用相同的内存位置,但该位置有两个引用。对其中一个的任何改变都会改变另一个。解决此问题的方法是实现
Cloneable
并使用
.clone()
方法。

浅层复制
创建同一类的新实例,并将所有字段复制到新实例并返回它。对象类提供克隆方法并支持浅层复制。

ArrayList保存引用是。但您可以克隆它(注意!但它将是浅拷贝-对于内部对象,它也不会克隆)

然后像这样使用

JustToCheck ob1 = new JustToCheck();
JustToCheck ob2 = ob1;

List<JustToCheck> arr = new ArrayList<JustToCheck>();
arr.add(ob1);
arr.add(ob2);

System.out.println(arr.get(0).toString() + ": " + arr.get(1).toString());

try {
    arr.set(1, (JustToCheck) ob1.clone());
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}

System.out.println(arr.get(0).toString() + ": " + arr.get(1).toString());

PS-我已经在答案中检查了这个问题

有些东西是不可变的

爪哇6

JustToCheck ob1 = new JustToCheck();
JustToCheck ob2 = ob1;

List<JustToCheck> arr = new ArrayList<JustToCheck>();
arr.add(ob1);
arr.add(ob2);

System.out.println(arr.get(0).toString() + ": " + arr.get(1).toString());

try {
    arr.set(1, (JustToCheck) ob1.clone());
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}

System.out.println(arr.get(0).toString() + ": " + arr.get(1).toString());
example$JustToCheck@1c39a2d: example$JustToCheck@1c39a2d
example$JustToCheck@1c39a2d: example$JustToCheck@bf2d5e
String a = new String("a");
String b = a;
b += "b"; 
System.out.println("a = " + a); // a = a
System.out.println("b = " + b); // b = ab