我有一个对象数组。我知道对象由“引用”分配,数组由“值”分配。但是当我分配数组时,数组的每个元素都引用了对象,所以当我修改任一数组中的对象时,更改会反映在另一个数组中。
是否有一种克隆数组的简单方法,或者我是否必须通过它来克隆每个对象?
答案 0 :(得分:64)
$array = array_merge(array(), $myArray);
答案 1 :(得分:44)
复制数组时,已经复制了对相同对象的引用。但是,当你创建第二个数组时,听起来你想要浅拷贝深度复制第一个数组中引用的对象,所以你得到两个不同但相似的对象数组。
我现在能够提出的最直观的方法是循环;那里可能有更简单或更优雅的解决方案:
$new = array();
foreach ($old as $k => $v) {
$new[$k] = clone $v;
}
答案 2 :(得分:18)
您需要克隆对象以避免引用同一对象。
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;
}
答案 3 :(得分:13)
正如AndreKR所建议的,如果你已经知道你的数组包含对象,那么使用array_map()是最好的方法:
$clone = array_map(function ($object) { return clone $object; }, $array);
答案 4 :(得分:5)
我也选择了克隆。克隆数组不起作用(你可以考虑一些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);
然而,这可能有点冒险。
答案 5 :(得分:2)
你需要循环它(可能使用类似array_map()
的函数),没有PHP函数来自动执行数组的深层复制。
答案 6 :(得分:2)
我这样做了:
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
。
答案 7 :(得分:2)
这是我对一系列对象和克隆的最佳实践。通常,对于在数组中使用的每个对象(或接口)类都有一个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()
了。因此,请在重构代码时注意这一点。
答案 8 :(得分:2)
纯PHP 7.4> =解决方案:
fullName
答案 9 :(得分:0)
或
$nuarr = json_decode(json_encode($array));
但价格昂贵,我更喜欢Sebastien版本(array_map)
答案 10 :(得分:0)
对象在默认情况下通过指向传递,并不总是很容易克隆,尤其是因为它们可能具有循环引用。您可能更适合使用不同的数据结构选择。
对于那些提供浅拷贝解决方案的人来说,更简单的方法是:
$b = (array)$a;
对于深层拷贝,我不建议使用此解决方案:
$ nuarr = json_decode(json_encode($ array));
这是一份深层复制品。它只支持PHP类型的一个子集,并且会将对象交换到数组或数组,而这些对象可能不是您想要的,也可能会破坏二进制值等等。
如果为深拷贝创建手动递归函数,那么标量值和键之后的内存使用量会更少,因此使用json或任何序列化程序会产生超出其执行点的影响。
如果性能不是一个对对象之类的东西有更广泛支持的问题,那么对深度拷贝使用unserialize(序列化($ a))可能会更好,尽管如果它打破循环引用和其他几个我不会感到惊讶不寻常的事情。
array_merge_recursive或array_walk_recursive也可用于数组。
您可以轻松创建自己的递归函数,该函数使用is_object和is_array来选择适当的复制方法。
答案 11 :(得分:0)
对于PHP 5及以上版本,可以使用ArrayObject
cunstructur来克隆数组,如下所示:
$myArray = array(1, 2, 3);
$clonedArray = new ArrayObject($myArray);
答案 12 :(得分:0)
答案 13 :(得分:0)
只需在所有类中都包含此功能。如果对象本身中有对象数组,则这将对所有对象进行深度克隆。它将触发这些类中的所有__clone()
函数:
/**
* Clone the object and its properties
*/
public function __clone()
{
foreach ($this as $key => $property)
{
if(is_array($property))
{
foreach ($property as $i => $o)
{
if(is_object($o)) $this->$key[$i] = clone $o;
else $this->$key[$i] = $o;
}
}
else if(is_object($property)) $this->$key = clone $property;
else $this->$key = $property;
}
}
答案 14 :(得分:-1)
$a = ['a'=>'A','b'=>'B','c'=>'C'];
$b = $a+[];
$a['a'] = 'AA'; // modifying array $a
var_export($a);
var_export($b);
结果:
array ( 'a' => 'AA', 'b' => 'B', 'c' => 'C', )
array ( 'a' => 'A', 'b' => 'B', 'c' => 'C', )