PHP关于foreach复制知识点总结


Posted in PHP onJanuary 28, 2019

PHP的foreach是一个非常整洁和切中要害的语言结构。仍然有些人不喜欢使用它,因为他们认为它是缓慢的。一个通常命名的原因是foreach复制它迭代的数组。

因此,一些人建议写:

$keys = array_keys($array);
$size = count($array);
for ($i = 0; $i < $size; $i++) {
  $key  = $keys[$i];
  $value = $array[$key];
 
  // ...
}

而不是更直观和直接:

foreach ($array as $key => $value) {
  // ...
}

这里有两个问题:

Microoptimization是不好的。通常,它只会浪费您的时间,不会带来任何可度量的性能改进。

foreach的复制行为比大多数人认为的要复杂一些。通常情况下,“优化”的版本会比原始版本慢。

foreach什么时候复制?

foreach是否复制数组以及复制的数量取决于三件事:

是否引用了迭代数组、它的refcount有多高以及迭代是否通过引用完成。

没有引用,refcount == 1

在下面的代码中,$array没有被引用,并且refcount为1。在这种情况下,foreach不会复制数组(证明)——这与流行的观点相反,即foreach总是复制没有引用的迭代数组。

test();
function test() {
  $array = range(0, 100000);
  foreach ($array as $key => $value) {
    // ...
  }
}

原因很简单:为什么要这样做?foreach修改$array的唯一地方是它是内部数组指针。这是预期的行为,因此不需要预防。

未引用,refcount > 1

下面的代码看起来非常类似于前面的代码。唯一的区别是数组现在作为参数传递。这似乎是一个无关紧要的区别,但它确实改变了foreach的行为:

它现在将复制数组结构,而不是值(证明;如果你想知道这只是复制的结构,比较一下这个和那个脚本。第一个只复制结构,第二个两个都复制)。

$array = range(0, 100000);
test($array);
function test($array) {
  foreach ($array as $key => $value) {
    // ...
  }
}

乍一看这可能有点奇怪:

为什么当数组通过参数传递时,它会复制,但如果它是在函数中定义的,它就不会复制了?原因是数组zval现在在多个变量之间共享:函数外部的$array变量和函数内部的$array变量。如果foreach在不复制数组结构的情况下迭代数组,那么它不仅会改变函数中$array变量的数组指针,还会改变函数外$array变量的指针。因此foreach需要复制数组结构(即散列表)。另一方面,这些值仍然可以共享zvals,因此不需要复制。

引用

下一种情况与前一种情况非常相似。唯一的区别是数组是通过引用传递的。在这种情况下,数组将不会被复制(证明)。

$array = range(0, 100000);
test($array);
function test(&$array) {
  foreach ($array as $key => $value) {
    // ...
  }
}

在这种情况下,相同的推理适用于前一种情况:外部$数组和内部$数组共享zvals。不同的是,它们现在是引用(isref == 1),因此在这种情况下,对内部数组的任何更改都将对外部数组进行。所以如果内部数组的数组指针改变了,外部数组的数组指针也应该改变。这就是foreach不需要复制的原因。

迭代通过引用

上面的例子都是按值迭代的。对于引用迭代,应用相同的规则,但是附加值引用更改数组值的复制行为(关于结构复制的行为保持不变)。

情况“未引用,refcount == 1”没有改变。引用迭代意味着如果$值有任何变化,我们想要改变原始数组,这样数组就不会被复制(证明)。

“被引用”的情况也保持不变,在这种情况下,对$value的更改应该会更改引用迭代数组的所有变量(证明)。

只有“未引用,refcount > 1”的情况发生了变化,因为现在需要复制数组结构及其值。数组结构,因为否则函数外部的$array变量的数组指针会改变,而对$value的改变也会改变外部的$array值(证明)。

总结

当且仅当迭代数组未被引用且具有refcount > 1时,foreach将复制数组结构

foreach还将复制数组值,前提是且仅当上一个点应用并且迭代是通过引用完成时

PHP 相关文章推荐
用PHP动态生成虚拟现实VRML网页
Oct 09 PHP
PHP注释实例技巧
Oct 03 PHP
基于PHP文件操作的详解
Jun 05 PHP
CI框架中site_url()和base_url()的区别
Jan 07 PHP
php截取中文字符串函数实例
Feb 23 PHP
PHP结合jQuery.autocomplete插件实现输入自动完成提示的功能
Apr 27 PHP
php微信浏览器分享设置以及回调详解
Aug 01 PHP
PHP实现微信模拟登陆并给用户发送消息的方法【文字,图片,图文】
Jun 29 PHP
Bootstrap+PHP实现多图上传功能实例详解
Apr 08 PHP
Laravel框架执行原生SQL语句及使用paginate分页的方法
Aug 17 PHP
详解PHP神奇又有用的Trait
Mar 25 PHP
php创建多级目录与级联删除文件的方法示例
Sep 12 PHP
实例讲解PHP验证邮箱是否合格
Jan 28 #PHP
PHP将英文数字转换为阿拉伯数字实例讲解
Jan 28 #PHP
PHP实现一个轻量级容器的方法
Jan 28 #PHP
PDO::_construct讲解
Jan 27 #PHP
PDO::commit讲解
Jan 27 #PHP
PDO::beginTransaction讲解
Jan 27 #PHP
PHP的PDO大对象(LOBs)
Jan 27 #PHP
You might like
php数据库连接时容易出错的特殊符号问题
2010/09/01 PHP
php中限制ip段访问、禁止ip提交表单的代码分享
2014/08/22 PHP
PHP版本如何选择?应该使用哪个版本?
2015/05/13 PHP
帝国CMS留言板回复后发送EMAIL通知客户
2015/07/06 PHP
WordPress中对访客评论功能的一些优化方法
2015/11/24 PHP
PHP实现负载均衡session共享redis缓存操作示例
2018/08/22 PHP
jquery实现图片渐变切换兼容ie6/Chrome/Firefox
2013/08/02 Javascript
使用firebug进行调试javascript的示例
2013/12/16 Javascript
jquery控制页面部分刷新的方法
2015/06/24 Javascript
Javascript获取随机数的实现方法
2016/06/22 Javascript
Vue实现双向绑定的方法
2016/12/22 Javascript
使用jquery datatable和bootsrap创建表格实例代码
2017/03/17 Javascript
详解webpack分包及异步加载套路
2017/06/29 Javascript
vue.js异步上传文件前后端实现代码
2017/08/22 Javascript
JS实现的简单标签点击切换功能示例
2017/09/21 Javascript
微信小程序如何像vue一样在动态绑定类名
2018/04/17 Javascript
VUE+node(express)实现前后端分离
2019/10/13 Javascript
[49:21]TNC vs VG 2019DOTA2国际邀请赛淘汰赛 胜者组赛BO3 第三场 8.20.mp4
2019/08/22 DOTA
以一段代码为实例快速入门Python2.7
2015/03/31 Python
python魔法方法-自定义序列详解
2016/07/21 Python
Python连接SQLServer2000的方法详解
2017/04/19 Python
mac安装scrapy并创建项目的实例讲解
2018/06/13 Python
几个适合python初学者的简单小程序,看完受益匪浅!(推荐)
2019/04/16 Python
使用python来调用CAN通讯的DLL实现方法
2019/07/03 Python
Python SSL证书验证问题解决方案
2020/01/13 Python
德国大型的家具商店:Pharao24.de
2016/10/02 全球购物
北京麒麟网信息技术有限公司网络游戏测试面试题
2013/09/28 面试题
口头翻译求职人自荐信
2013/12/07 职场文书
酒店副总经理岗位职责范本
2014/02/04 职场文书
淘宝店铺营销方案
2014/02/13 职场文书
平面设计求职信
2014/03/10 职场文书
部门年终奖分配方案
2014/05/07 职场文书
离婚协议书范本样本
2014/08/19 职场文书
高中教师个人总结
2015/02/10 职场文书
技术员个人工作总结
2015/03/03 职场文书
Java由浅入深通关抽象类与接口(上篇)
2022/04/26 Java/Android