对象数组的深拷贝
Posted
技术标签:
【中文标题】对象数组的深拷贝【英文标题】:Deep copy of an object array 【发布时间】:2011-04-26 05:21:06 【问题描述】:我想使用构造函数制作对象数组的深层副本。
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];
但是,由于某种原因,我上面的内容不起作用。我有我运行的自动化测试,但它没有通过这些测试。所以这里有一个错误,我不确定它是什么。
【问题讨论】:
【参考方案1】:当你说:
data[i]=other.data[i];
您只是在复制一个引用列表(假设这是一个对象数组)。如果要进行深拷贝,则需要使用new
为数组中的每个对象创建一个新实例。
【讨论】:
但是我应该在哪里使用 new 呢?在循环中? 是的。在每次迭代中,创建一个new
对象并将其添加到data
。
好吧,我很困惑。那么我会为上面的代码删除data=new Position[other.data.length];
并以某种方式将其添加到循环中吗?【参考方案2】:
而不是说:
data[i]=other.data[i]
您需要为 Position
创建一个复制构造函数(换句话说,一个 Position 的构造函数接受另一个 Position
并复制其中的原始数据)并说 data[i]=new Position(other.data[i]);
基本上你的“深拷贝”构造函数PositionList
是一个拷贝构造函数,虽然拷贝构造函数确实倾向于表示一个深拷贝,所以deepCopy
参数是不必要的。
【讨论】:
-1 克隆生成浅拷贝而不是深拷贝。它最终可以适用于基元和不可变对象,但不要将其与深层副本混淆。 @bwawok:没错,我假设Position
是一个POJO,因此.clone()
可以工作。
没有克隆怎么办?
@fprime:然后使用复制构造函数,正如我的替代解决方案所说。也就是说,我真的应该把“复制构造函数”解决方案放在第一位,因为.clone()
确实有缺点。【参考方案3】:
您实施的是浅副本。要实现深度副本,您必须 改变
data[i] = other.data[i];
将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();
注意事项:
-
以上假设复制构造函数、工厂方法和克隆方法分别实现了“正确”的复制,具体取决于 Position 类;见下文。
clone
方法只有在Position
明确支持的情况下才有效,这通常被认为是一种较差的解决方案。此外,您需要注意clone
的本机实现(即Object.clone()
方法)会进行浅拷贝1。
事实上,在 Java 中实现深度复制的一般问题是复杂的。在Position
类的情况下,人们会假设属性都是原始类型(例如整数或双精度数),因此深复制与浅复制没有实际意义。但是如果有引用属性,那么您必须依靠复制构造函数/工厂方法/克隆方法来执行您需要的那种复制。在每种情况下都需要对其进行编程。而在一般情况下(您必须处理循环),这很困难,并且需要每个类实现特殊方法。
还有另一种潜在方法可以复制对象数组。如果数组中的对象是serializable,那么你可以通过使用ObjectOutputStream
和ObjectInputStream
序列化来复制它们,然后对数组进行反序列化。然而:
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 所说,没有任何魔法。您实现了一个构造函数(手动),它通过从现有实例复制来初始化其状态。
1 - 指定 clone()
的 Object 实现执行浅拷贝,但这可能会被覆盖。 clone
的 javadoc 指定“合同”如下:
“创建并返回此对象的副本。“副本”的确切含义可能取决于对象的类。一般意图是,对于任何对象 x,表达式:@ 987654340@ 为真,表达式:
x.clone().getClass() == x.getClass()
为真,但这些不是绝对要求。虽然通常情况下:x.clone().equals(x)
为真,但这不是绝对要求。” em>
“合同”中没有提到深拷贝和浅拷贝。所以如果你打算在这个上下文中使用clone
,你需要知道clone
方法的实际类的行为。
【讨论】:
复制构造函数是我需要的。你上面的那个工作得很好,但我不明白。所以我们正在创建一个新位置,但是我们提供给它的参数是什么,other.data[i]?你能描述一下它是如何工作的吗? @fprime:这不是魔法。有人需要为 Position 类编写一个构造函数,该构造函数将该类的实例作为其唯一参数并返回一个深层副本。 不一定 clone() 做深拷贝:例如,返回这个 ArrayList 实例的浅拷贝。 (元素本身不会被复制。) 关于第三个选项,clone()
- 确保 Point 实现 Cloneable
。
1) No. 2) 鉴于没有复制Object
的通用方法,因此不可能“深度复制”任意Object[]
。【参考方案4】:
这应该是一个“深”的副本
int [] numbers = 2, 3, 4, 5;
int [] numbersClone = (int[])numbers.clone();
【讨论】:
这适用于基元数组(如整数),但问题是关于对象数组。【参考方案5】:这是我使用的一个函数:
function copy(arr)
return arr
.map(x => Object
.keys(x)
.reduce((acc, y) =>
acc[y] = x[y]
return acc
, ))
它仅适用于具有单层对象的数组。
【讨论】:
以上是关于对象数组的深拷贝的主要内容,如果未能解决你的问题,请参考以下文章