我有一个对象数组。 我知道对象由"引用"分配,数组由"值"分配。 但是,当我分配数组时,数组的每个元素都引用该对象,因此,当我修改任一数组中的对象时,更改将反映在另一个数组中。
有没有简单的方法来克隆数组,还是必须遍历它才能克隆每个对象?
对对象的引用是按值分配的。 unnecessarypedantry>
$array = array_merge(array(), $myArray);
太聪明了!比foreach循环干净得多!
这实际上与$ array = $ myArray不同吗?见stackoverflow.com/questions/1532618/
@GeorgeLund不,这没什么不同。参见php.net/manual/en/function.array-merge.php#92346
排队的海绵宝宝旁白:"六个小时后……"我遇到了另一个问题,我的旅途将我带回到了这里。谁会重击?我需要一种在保留所有引用的同时保留内部指针不变的同时克隆数组的方法。正是这样做的。 +1。
经过一夜安眠后,我意识到我不需要array_merge()。我已经为我的解决方案添加了对类似问题的答案:stackoverflow.com/a/17729234/1134804
在此解决方案中,我们丢失了$ myArray的原始数字键:"输入数组中带有数字键的值将使用从结果数组中的零开始的递增键重新编号。"
这不是克隆数组中的对象(就像有人问的那样)!所以我不明白这个答案和高票?就像$ arrB = $ arrA几乎一样,只是数字键被重新索引了。
更聪明:$ array =(array)$ myArray;
最聪明的人:仔细阅读问题,并意识到这个答案并不能解决问题所要解决的问题,也不能提供这些注释中的变化!
复制数组时,已经复制了对相同对象的引用。但这听起来像是您想在创建第二个数组时shallow-copy strike>深度复制在第一个数组中引用的对象,以便获得两个不同但相似的对象的数组。
我现在想出的最直观的方法是循环。可能会有更简单或更优雅的解决方案:
$new = array();
foreach ($old as $k => $v) {
$new[$k] = clone $v;
}
他现在正在做的是浅复制,他想进行深复制。您的代码是正确的,但是您的术语不是。
@Kristian Antonsen:我不是在浅拷贝对象,而不仅仅是在浅拷贝数组吗?
@BoltClock:是否将对象浅层复制取决于特定对象的克隆实现;那取决于对象的设计者,而不是用户。例如,如果数组中的一个对象包含对另一个对象的引用,并且尚未实现适当的__clone函数,则该函数仍然只能是浅表副本。换句话说:如果您克隆的对象的开发人员正确执行了操作,则它将是深拷贝。
@BoltClock不,您需要深度复制数组和数组元素。无论如何,您的答案代码是正确的。
知道了,我忘记了PHP提供了一种用于实现深层复制的__clone()方法,并且假定了clone的默认浅层复制行为。
@DisgruntledGoat只知道:clone并非总是可以创建独立副本,请阅读手册:Any properties that are references to other variables, will remain references.
我只需要每个对象的浅表副本(对象仅包含值),因此此答案可以解决问题。
您需要克隆对象以避免引用相同的对象。
function array_copy($arr) {
$newArray = array();
foreach($arr as $key => $value) {
if(is_array($value)) $newArray[$key] = array_copy($value);
else if(is_object($value)) $newArray[$key] = clone $value;
else $newArray[$key] = $value;
}
return $newArray;
}
简单但有效。我将其用作静态函数。
+1我不小心盗用了您的功能而未意识到:stackoverflow.com/a/17729234/1134804。我曾多次浏览过此页面,但我一直向下滚动,直到找到一些投票率很高的答案(因此跳过您的答案)。它几乎具有完全相同的功能,直到现在,我什至从未看过您的答案!
正如AndreKR所建议的那样,如果您已经知道数组包含对象,则使用array_map()是最好的方法:
$clone = array_map(function ($object) { return clone $object; }, $array);
我也选择克隆。克隆数组是行不通的(您可以考虑使用一些arrayaccess实现来实现),因此对于使用array_map的数组克隆:
class foo {
public $store;
public function __construct($store) {$this->store=$store;}
}
$f = new foo('moo');
$a = array($f);
$b = array_map(function($o) {return clone $o;}, $a);
$b[0]->store='bar';
var_dump($a, $b);
具有序列化和反序列化的阵列克隆
如果您的对象支持序列化,那么您甚至可以进行深度浅的复制/克隆,并浏览其休眠状态并返回:
$f = new foo('moo');
$a = array($f);
$b = unserialize(serialize($a));
$b[0]->store='bar';
var_dump($a, $b);
但是,这可能有点冒险。
这是我对一系列对象和克隆的最佳实践。通常,对于在数组中使用的每个对象(或接口)类都有一个Collection类,这是一个好主意。使用__clone的魔术函数,克隆成为正式的例程:
class Collection extends ArrayObject
{
public function __clone()
{
foreach ($this as $key => $property) {
$this[$key] = clone $property;
}
}
}
要克隆您的阵列,请将其用作Collection,然后对其进行克隆:
$arrayObject = new Collection($myArray);
$clonedArrayObject = clone $arrayObject;
更进一步,您还应该向您的类和每个子类添加一个克隆方法。这对于深度克隆很重要,否则可能会有意外的副作用:
class MyClass
{
public function __clone()
{
$this->propertyContainingObject = clone $this->propertyContainingObject;
}
}
关于使用ArrayObject的重要说明是,您不能再使用is_array()。因此,在重构代码时请注意这一点。
我这样做是这样的:
function array_clone($array) {
array_walk_recursive($array, function(&$value) {
if(is_object($value)) {
$value = clone $value;
}
});
return $array;
}
函数arg复制数组而不克隆对象,
然后克隆每个嵌套对象。因此,如果在函数内部不使用该算法,它将无法正常工作。
请注意,此函数以递归方式克隆数组。如果您不希望发生这种情况,可以使用array_walk代替array_walk_recursive。
您需要循环它(可能使用类似array_map()的函数),没有PHP函数可以自动执行数组的深层复制。
如果您具有多维数组或由对象和其他值组成的数组,则可以使用此方法:
$cloned = Arr::clone($array);
从那个图书馆。
对于PHP 5及更高版本,可以使用ArrayObject cunstructur来克隆数组,如下所示:
$myArray = array(1, 2, 3);
$clonedArray = new ArrayObject($myArray);
默认情况下,对象是按指针传递的,并且并不总是易于克隆,尤其是因为它们可能具有循环引用。您将更适合使用其他选择的数据结构。
对于那些提供浅拷贝解决方案的人来说,更简单的方法是:
$b = (array)$a;
对于深拷贝,我不建议此解决方案:
$nuarr = json_decode(json_encode($array));
这是一个深复制。它仅支持PHP类型的子集,并且会将对象交换到数组或将数组交换到对象,而这可能不是您想要的,并且可能破坏二进制值,等等。
如果您为深层副本创建手动递归函数,那么标量值和键之后的内存使用量将大大减少,因此使用json或任何序列化程序将对执行产生影响。
如果不关心性能(它对对象等事物有更广泛的支持),则最好对深层副本使用unserialize(serialize($ a)),尽管它不会破坏循环引用和其他一些异常的事物,但我不会感到惊讶。
array_merge_recursive或array_walk_recursive也可以用于数组。
您可以轻松创建自己的递归函数,该函数使用is_object和is_array选择适当的复制方式。
或者也
但价格昂贵,我更喜欢Sebastien版本(array_map)