php按值复制数组元素,而不是按引用

Posted

技术标签:

【中文标题】php按值复制数组元素,而不是按引用【英文标题】:php copying array elements by value, not by reference 【发布时间】:2010-11-14 11:19:00 【问题描述】:

我有以下代码:

$data['x'] = $this->x->getResults();  

$data['y'] = $data['x'];

//some code here to modify $data['y']
//this causes (undesirably) $data['x] to be modified as well

我猜因为 $data 的所有元素本身都是引用,所以修改 $data['y'] 也会修改 $data['x']..这不是我想要的。我希望 $data['x'] 保持不变。有什么方法可以在这里取消引用元素,以便我可以按值复制元素?

谢谢。

更新:$this->x->getResults();返回一个对象数组。所以我可以这样做: $data['x'][0]->date_create ...

更新: 我最近一次克隆数组的尝试是这样的:

   $data['x'] = $this->x->getResults();     
   $data['y'] = $data['y'];
   foreach($data['x'] as $key=>$row) 
       $data['y'][$key]->some_attr = clone $row->some_attr;
   

我离这儿很远吗?我不断收到“在非对象上调用 __clone 方法”错误。从阅读回复看来,我最好的选择是遍历每个元素并克隆它(这是我试图用该代码做的事情..)。

更新:刚刚解决了!:在 foreach 循环中,我只需要将行更改为:

$data['y'][$key] = clone $row;

而且它有效!感谢大家的帮助。

【问题讨论】:

$this->x->getResults() 返回的是什么?一个对象? 了解$this->x->getResults() 返回的内容很重要... 是的,我正在使用 codeigniter,所以这是对模型 (x) 的调用,它返回数据库查询结果的对象数组 那么您必须克隆该对象数组中的每个对象。 在编写自己的类时,默认情况下不会克隆内部变量引用。您必须实现克隆功能,例如:public function __clone() $this->widget = clone $this->widget(); 用于对象内的每个引用也应该被克隆。 【参考方案1】:

您可以利用 php 将取消引用函数调用的结果这一事实。

这是我编写的一些示例代码:

$x = 'x';
$y = 'y';
$arr = array(&$x,&$y);
print_r($arr);

echo "<br/>";
$arr2 = $arr;
$arr2[0] = 'zzz';
print_r($arr);
print_r($arr2);

echo "<br/>";
$arr2 = array_flip(array_flip($arr));
$arr2[0] = '123';
print_r($arr);
print_r($arr2);

结果如下所示:

数组( [0] => x [1] => y ) 数组 ( [0] => zzz [1] => y ) 数组 ( [0] => zzz [1] => y ) 数组 ( [0] => zzz [1] => y ) 数组 ( [0] => 123 [1] => y )

您可以看到,在将$arr 分配给$arr2 期间使用array_flip() 的结果会导致对$arr2 的后续更改存在差异,因为array_flip() 调用会强制取消引用。

它看起来效率不高,但如果 $this-&gt;x-&gt;getResults() 返回一个数组,它可能对你有用:

$data['x'] = array_flip(array_flip($this->x->getResults()));
$data['y'] = $data['x'];

另一个例子见this (unanswered) thread。

如果您返回的数组中的所有内容都是对象,那么复制对象的唯一方法是使用clone(),并且您必须遍历$data['x'] 并将每个元素克隆到$data['y']

例子:

$data['x'] = $this->x->getResults();
$data['y'] = array();
foreach($data['x'] as $key => $obj) 
    $data['y'][$key] = clone $obj;

【讨论】:

我遇到了一些麻烦:就像你在上一句中提到的那样,我返回的数组中的所有内容都是一个对象..你可以发布一些代码来展示如何遍历数组并克隆每个元素?我不断收到“在非对象上调用 _clone 方法”错误。谢谢。 没问题,我加了一个例子。 clone 只对实际对象起作用,所以如果$this-&gt;x-&gt;getResults(); 的返回结果是一个对象数组而不是一个对象,那么你不能克隆数组,你必须单独克隆数组的每个元素。跨度> array_flip 不是一个好主意!!如果您有 2 个具有相同值的键,则在调用 array_flip 函数时第一个键将丢失。 这里有一些错误信息。函数 return 不做深度解引用。上面的魔法是array_flip(array_flip($array))..【参考方案2】:

array_flip() 在数组值既不是字符串也不是整数时不起作用。 但是,我找到了一个简单的解决方案:

$clonedArr = (array)clone(object)$arr;

这要归功于对象上的克隆属性。

【讨论】:

这不起作用。如果$arr 中包含引用变量,则新数组仍将包含它们。引用在克隆后仍然存在。【参考方案3】:

不简单。 阅读clone

但是!如果您的元素是不是对象并且不是引用类型变量,那么您就没有问题。

引用类型示例:

$v=11;
$arr[]=&$v;

【讨论】:

为什么只有元素是对象时我才有问题?是因为对象本身就是引用吗? 在 PHP 中,(从版本 5 IICRC 开始)默认情况下所有对象都通过引用传递。 这个答案并不完全正确。它应该是“如果你的元素不是引用,你就没有问题。”。您可以拥有不是对象的引用。 对象不是引用,它们在技术上是指针,但它们看起来像引用。【参考方案4】:

如果您正在使用对象,您可能需要查看clone,以创建对象的副本,而不是引用

这是一个非常简短的例子:

首先,使用数组,它按值工作:

$data['x'] = array(
    'a' => 'test',
    'b' => 'glop',
);
$data['y'] = $data['x'];
$data['y'][0] = 'Hello, world!';
var_dump($data['x']); // a => test : no problem with arrays

默认情况下,对于对象,它通过引用工作:

$data['x'] = (object)array(
    'a' => 'test',
    'b' => 'glop',
);
$data['y'] = $data['x'];
$data['y']->a = 'Hello, world!';
var_dump($data['x']); // a => Hello, world! : objects are by ref

但是,如果您克隆对象,您将在副本上工作: 我猜这就是你的情况?

$data['x'] = (object)array(
    'a' => 'test',
    'b' => 'glop',
);
$data['y'] = clone $data['x'];
$data['y']->a = 'Hello, world!';
var_dump($data['x']); // a => test : no ref, because of cloning

希望这会有所帮助,

【讨论】:

感谢代码示例..你知道克隆功能是否相对高效吗?我问是因为我知道在 java 中,如果可能的话,通常最好避免这种情况。 我不知道效率,但是如果您需要功能,那并不重要,不是吗? (而且,考虑到您刚才正在执行数据库查询,与脚本的其余部分相比,它不应该那么长:-)) 我认为唯一固有的低效率是由于您将内存量加倍以将同一对象存储两次。因此,它的效率与复制任何其他变量一样低。【参考方案5】:

我刚刚发现,如果你只是想从一个常量中复制一个值数组(没有引用),那么你可以这样写:

$new_array = (array) (object) self::old_array;

不是对 OP 问题的确切答案,但它帮助了我并且可能帮助其他人。

【讨论】:

【参考方案6】:

array_merge() 可以接受任意数量的参数,甚至是 1,然后生成一个新数组。所以只需执行以下操作:

$new_array = array_merge($existing_array);

【讨论】:

【参考方案7】:

您可以使用此函数复制包含对象的多维数组。

<?php
function arrayCopy( array $array ) 
    $result = array();
    foreach( $array as $key => $val ) 
        if( is_array( $val ) ) 
            $result[$key] = arrayCopy( $val );
         elseif ( is_object( $val ) ) 
            $result[$key] = clone $val;
         else 
            $result[$key] = $val;
        
    
    return $result;

?>

【讨论】:

clone 不做浅拷贝吗?所以它不会递归到任何数组属性。 是的,不会。所以我想如果有任何包含更多对象的数组属性,你可以使用__clone() 让对象自己弄清楚它们应该如何被克隆。

以上是关于php按值复制数组元素,而不是按引用的主要内容,如果未能解决你的问题,请参考以下文章

如何按值而不是按引用复制对象[重复]

按值而不是按引用复制对象

按引用传递然后复制和按值传递在功能上是不是不同?

Java函数参数是不是总是按值传递数组? [复制]

如何按值而不是按引用将数组添加到列表中?

PHP 中的数组是作为值复制还是作为对新变量的引用,以及何时传递给函数?