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缩略图生成程式(需要GD库支持)
Mar 06 PHP
php Ajax乱码
Apr 09 PHP
使用PHP提取视频网站页面中的FLASH地址的代码
Apr 17 PHP
PHP下打开URL地址的几种方法小结
May 16 PHP
php Rename 更改文件、文件夹名称
May 24 PHP
解析PHP提交后跳转
Jun 23 PHP
使用PHP实现蜘蛛访问日志统计
Jul 05 PHP
php函数间的参数传递(值传递/引用传递)
Sep 23 PHP
php获取远程图片体积大小的实例
Nov 12 PHP
PHP中数据类型转换的三种方式
Apr 02 PHP
PHP常见的6个错误提示及解决方法
Jul 07 PHP
PHP 进程池与轮询调度算法实现多任务的示例代码
Nov 26 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 Switch 语句之学习笔记
2013/09/21 PHP
PHP获取MAC地址的具体实例
2013/12/13 PHP
PHP中的socket_read和socket_recv区别详解
2015/02/09 PHP
PHP.ini安全配置检测工具pcc简单介绍
2015/07/02 PHP
“不能执行已释放的Script代码”错误的原因及解决办法
2007/09/09 Javascript
浅谈EasyUI中编辑treegrid的方法
2015/03/01 Javascript
jquery+php实现滚动的数字特效
2015/11/29 Javascript
详解AngularJS实现表单验证
2015/12/10 Javascript
jquery if条件语句的写法
2016/05/19 Javascript
Bootstrap table学习笔记(2) 前后端分页模糊查询
2017/05/18 Javascript
详解基于vue-cli优化的webpack配置
2017/11/06 Javascript
JavaScript去掉数组重复项的方法分析【测试可用】
2018/07/19 Javascript
在JavaScript中实现链式调用的实现
2019/12/24 Javascript
JavaScript实现京东快递单号查询
2020/11/30 Javascript
javascript实现简单留言板案例
2021/02/09 Javascript
[51:00]Secret vs VGJ.S 2018国际邀请赛淘汰赛BO3 第一场 8.24
2018/08/25 DOTA
python生成指定尺寸缩略图的示例
2014/05/07 Python
关于Python元祖,列表,字典,集合的比较
2017/01/06 Python
Python基于FTP模块实现ftp文件上传操作示例
2018/04/23 Python
numpy中以文本的方式存储以及读取数据方法
2018/06/04 Python
Python实现二维曲线拟合的方法
2018/12/29 Python
Python+AutoIt实现界面工具开发过程详解
2019/08/07 Python
python或C++读取指定文件夹下的所有图片
2019/08/31 Python
wxPython实现列表增删改查功能
2019/11/19 Python
使用python代码进行身份证号校验的实现示例
2019/11/21 Python
基于Jquery和Css3代码制作可以缩放的搜索框
2015/11/19 HTML / CSS
全球最大最受欢迎的旅游社区:Tripadvisor
2017/11/03 全球购物
GANT英国官方网上商店:甘特衬衫
2018/02/06 全球购物
EQVVS官网:设计师男装和女装
2018/10/24 全球购物
财务副总经理工作职责
2013/11/25 职场文书
竞选演讲稿范文
2013/12/28 职场文书
乡镇庆八一活动方案
2014/02/02 职场文书
2014中学教师节广播稿
2014/09/10 职场文书
死亡证明书样本说明
2014/10/18 职场文书
小学生五一劳动节演讲稿
2015/03/18 职场文书
tp5使用layui实现多个图片上传(带附件选择)的方法实例
2021/11/17 PHP