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截取utf-8中文字符串乱码的解决方法
Mar 29 PHP
第三章 php操作符与控制结构代码
Dec 30 PHP
浅谈web上存漏洞及原理分析、防范方法(安全文件上存方法)
Jun 29 PHP
php实现图片添加水印功能
Feb 13 PHP
如何使用jQuery+PHP+MySQL来实现一个在线测试项目
Apr 26 PHP
Zend Framework实现具有基本功能的留言本(附demo源码下载)
Mar 22 PHP
浅谈php处理后端&amp;接口访问超时的解决方法
Oct 29 PHP
PHP preg_match实现正则表达式匹配功能【输出是否匹配及匹配值】
Jul 19 PHP
windows环境下使用Composer安装ThinkPHP5
May 18 PHP
PHP等比例压缩图片的实例代码
Jul 26 PHP
PHP全局使用Laravel辅助函数dd
Dec 26 PHP
PHP const定义常量及global定义全局常量实例解析
May 28 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水印技术
2007/02/14 PHP
PHP中使用memcache存储session的三种配置方法
2014/04/05 PHP
php中实现记住密码下次自动登录的例子
2014/11/06 PHP
PHP简单实现断点续传下载的方法
2015/09/25 PHP
php如何连接sql server
2015/10/16 PHP
php生成验证码,缩略图及水印图的类分享
2016/04/07 PHP
PHP用户注册邮件激活账户的实现代码
2017/05/31 PHP
原型方法的不同写法居然会影响调试的解决方法
2007/03/08 Javascript
javascript 循环调用示例介绍
2013/11/20 Javascript
jQuery(js)获取文字宽度(显示长度)示例代码
2013/12/31 Javascript
Extjs Label的 fieldLabel和html属性值对齐的方法
2014/06/15 Javascript
jQuery将多条数据插入模态框的示例代码
2014/09/25 Javascript
node.js中的fs.readSync方法使用说明
2014/12/17 Javascript
让图片跳跃起来  javascript图片轮播特效
2016/02/16 Javascript
详谈jQuery中的一些正则匹配表达式
2017/03/08 Javascript
Vue绑定内联样式问题
2018/10/17 Javascript
js实现从右往左匀速显示图片(无缝轮播)
2020/06/29 Javascript
Python实现简单的可逆加密程序实例
2015/03/05 Python
python实现复制整个目录的方法
2015/05/12 Python
关于django 数据库迁移(migrate)应该知道的一些事
2018/05/27 Python
Python GUI布局尺寸适配方法
2018/10/11 Python
python 循环读取txt文档 并转换成csv的方法
2018/10/26 Python
python 利用pandas将arff文件转csv文件的方法
2019/02/12 Python
Python sublime安装及配置过程详解
2020/06/29 Python
python 如何设置守护进程
2020/10/29 Python
CSS3控制HTML元素动画效果
2014/02/08 HTML / CSS
美国室内和室外装饰花盆购物网站:ePlanters
2019/03/22 全球购物
兰蔻俄罗斯官方网站:Lancome俄罗斯
2019/12/09 全球购物
电话客服专员岗位职责
2014/06/28 职场文书
2014领导班子四风问题查摆思想汇报
2014/09/13 职场文书
离婚承诺书格式范文
2015/05/04 职场文书
幼儿园师德师风心得体会
2016/01/12 职场文书
Spring-cloud Config Server的3种配置方式
2021/09/25 Java/Android
Java字符串逆序方法详情
2022/03/21 Java/Android
【海涛教你打DOTA】剑圣第一人称视角解说
2022/04/01 DOTA
springboot读取nacos配置文件
2022/05/20 Java/Android