我一直在想这个问题。我通常会根据自己的直觉,在遍历数组键的同时更改数组:
可以在foreach调用中通过引用传递数组项:
foreach (array_keys($array) as $key) {
$array[$key] = perform_changes_on($array[$key]);
}
最后,可以在循环期间直接修改数组:
foreach ($array as &$item) {
$item = perform_changes_on($item);
}
每种方法对性能和安全性有何影响?有没有推荐的方法?
更新:我实际上担心的是,在循环时,最后两种方法会修改数组。
#1 楼
如果您担心在循环时修改数组,请不要这样做,因为foreach会复制数组。尝试$array = array('foo');
foreach ($array as $k => &$item) {
$array[] = 'bar';
}
var_dump($array);
,看看它终止得很好。
foreach ($array as $k => &$v)
是foreach (array_keys($array) as $k) $v = &$array[$k]
的简写,因此虽然仍然有数组的副本(这就是为什么我在示例中使用&$item
的原因,所以您可以看到,如果您修改了数组,那么它将在引用中被修改!$array = array('foo', 'bar');
foreach ($array as $k => $item) {
echo "$item\n";
if (!$k) {
$array[1] = 'baz';
}
}
$array = array('foo', 'bar');
foreach ($array as $k => &$item) {
echo "$item\n";
if (!$k) {
$array[1] = 'baz';
}
}
第一个转储foo和bar,第二个foo和baz。
评论
\ $ \ begingroup \ $
“ foreach($ array as $ k => $ v)是foreach(array_keys($ array)as $ k)$ v =&$ array [$ k]的简写。”与调用函数和语言构造的区别不大一样(尽管出于实际原因,我认为这只是简写)。同样,它是副本的简写,而不是参考。 (此外,PHP是写时复制的,因此您通常可以忽略多余的副本。)
\ $ \ endgroup \ $
– Corbin
2012年6月20日在1:46
\ $ \ begingroup \ $
显然,这是要对foreach($ array如$ k =>&$ v)进行编辑和修复的。
\ $ \ endgroup \ $
–chx
2012年6月20日下午5:46
\ $ \ begingroup \ $
要指出,if(!$ k)对于0为TRUE,否则该语句始终为false。因此,实质上,您在看到第二个数组元素之前就将其覆盖。
\ $ \ endgroup \ $
– mseancole
2012年6月20日下午13:31
\ $ \ begingroup \ $
我做到了,并且这样做是为了证明第一个foreach在副本中有效,而第二个则不能。
\ $ \ endgroup \ $
–chx
2012年6月20日在16:39
\ $ \ begingroup \ $
@chx然后您可能要运行第一个代码段。 $ item是$ array中值的副本。 $ array永远不会被复制(尽管如果foreach循环完成,将复制$ array的所有元素)。第一个片段将像第二个一样转储foo和baz。
\ $ \ endgroup \ $
– Corbin
2012年6月22日,下午3:33
#2 楼
这听起来像是放置array_map()
的地方。$array = array_map('perform_changes_on', $array);
评论
\ $ \ begingroup \ $
很好,但是有时候我不会更改数组中的每个项目。我只是想使示例更简单...
\ $ \ endgroup \ $
– Capi Etheriel
2012年6月18日17:55
\ $ \ begingroup \ $
perfom_changes_on不必更改每个元素。请注意,还有其他数组函数,其中一些函数比其他函数更强大。例如,array_reduce可以有很大帮助。
\ $ \ endgroup \ $
– Quentin Pradet
2012年6月19日在11:46
\ $ \ begingroup \ $
这不涉及对每个元素的函数调用吗?可能不是最好的性能明智的选择。
\ $ \ endgroup \ $
–汤姆
2014年12月8日在18:31
\ $ \ begingroup \ $
@Tom:我不会说函数调用的开销很明显。可维护性比非常小的性能提升更重要(我什至不确定它们是否确实存在)。但是,如果您认为编写不带函数的100,000行程序是最好的编程方法,则一定要这样做。当没有人想要维护代码时,不要感到惊讶,而且一天后您将不明白自己写的内容。
\ $ \ endgroup \ $
– Konrad Borowski
2014-12-8 20:34
#3 楼
使用foreach($array as &$item)
时,切勿忘记在foreach之后使用unset($item);
,否则以后尝试使用$item
时会遇到严重麻烦。 通常应该避免
foreach ...&
并执行array_walk($array, function (&$item) {...
,以便将引用严格限制在闭包内。评论
\ $ \ begingroup \ $
感谢您的回答,我正在使用array_keys来避免陷阱,但是我需要使用它吗?我的意思是,foreach(将$ array作为$ key => $ item)应该让我更改$ array [$ key]的值,但是谁知道php在内部如何工作……实际上就是问题所在。
\ $ \ endgroup \ $
– Capi Etheriel
2012年6月19日在17:02
#4 楼
对于性能方面,我真的不能帮您,只能告诉您将它们包装在microtime()
标签中,然后看看哪个对您来说效果更好。系统略有不同。我可以给您的一个建议是从您的代码中删除array_keys()
。为您的答案。我正在寻找,foreach感到困惑。 For和while循环确实会在每次迭代中调用作为参数传入的任何函数,而foreach则不会。这就是为什么最好在for或while循环之外调用strlen()
和count()
之类的函数来给出几个示例的原因。我们遇到的开销不是来自foreach,而是来自array_keys()
。调用array_keys()
时,它必须生成一个新的数组,这就是为什么它几乎慢一倍的原因。因此,最好将所有array_keys()
函数全部删除,然后仅遍历数组并检索键值对。很抱歉造成任何混乱。资料来源: .com / questions / 3430194 / performance-of-for-vs-foreach-in-php
https://stackoverflow.com/questions/1740575/php5-does-the-copy-of-an-array- for-for-each-incur-overhead
!!更新结束!
据我所知,所有这些实现都没有安全风险。您正在迭代一个已经存在的构造。在此之前,任何安全问题都会发生。当然,如果您使用的是用户提供的数据,例如GET和POST。您应该在使用之前清理并验证这些内容,这可以通过其中的每个foreach循环来完成。或者您也可以查看
filter_input_array()
及其表亲。我知道由于缺乏可读性,我个人不会使用第二种实现。至少在第一个和第三个实现中,您可以直观地看到一个值正在更改。第二个不是很明显。但是,很有可能效率更高。我自己使用了第一个和第三个,但更经常使用第三个。认为这与我的心情有关。希望这会有所帮助,我很想听听其他人可能要说的话:)
评论
\ $ \ begingroup \ $
“给foreach的参数是在每次迭代中声明的,因此从技术上讲,每次foreach循环时,您都在调用array_keys()。”不,在这种情况下,它将仅调用一次array_keys。 (也许在PHP5之前它就具有这种行为,但我不这样认为,因为这很容易以无限循环结束。)
\ $ \ endgroup \ $
– Corbin
2012年6月18日17:11
\ $ \ begingroup \ $
@Corbin:不,仍然是一个问题。不相信我,对其进行一些测试,您会发现自己的。 PHP每次循环时都会为第一个参数创建一个迭代器对象,以便它可以在每次迭代时执行each()。它被重新创建,但指针保持不变。这就是为什么如果您通过引用更改了数组的值,那么它将在下一次迭代时立即可用:)
\ $ \ endgroup \ $
– mseancole
2012年6月18日在18:09
\ $ \ begingroup \ $
您能举个例子吗?只是试图让它两次调用array_keys(好吧,f),却永远无法实现。
\ $ \ endgroup \ $
– Corbin
2012年6月18日在18:20
\ $ \ begingroup \ $
潜在相关:stackoverflow.com/questions/4043569/…
\ $ \ endgroup \ $
– Corbin
2012年6月18日18:35
\ $ \ begingroup \ $
@Corbin:老实说,我不确定。我见过有人声称完全相反,而我的速度测试似乎证明了这一点。具有“重新创建的”数组的foreach似乎总是需要更长的时间。进行了超过一百次迭代的测试,所有结果似乎都证明了在后台执行了额外的操作。我仍在寻找该职位,希望可以找到它。我需要开始保留这些帖子的存储库...
\ $ \ endgroup \ $
– mseancole
2012年6月18日19:17
#5 楼
我会采用第二种方法:foreach ($array as &$item) {
$item = perform_changes_on($item);
}
甚至更好:
function perform_changes_on(&$item) {
// ...
}
foreach ($array as &$item) {
perform_changes_on($item);
}
// ...
item(按引用)似乎是最安全的一项,因为您不访问数组的结构,而是访问其单个元素(我想这就是您想要的)。
评论
stackoverflow.com/questions/10057671/how-foreach-actually-works很有帮助。OP提供了实际的代码,但是由于代码不是“具体实现”的一部分而被关闭了吗?老实说,什么?