请选择 进入手机版 | 继续访问电脑版
绿茶科技社区
标题是不要在循环体中使用 array_push(),其实这只是本篇文章的结论之一
下面我们一起研究一下 php 语言中数组的追加元素
向数组追加元素
  1. 我们知道 php 在数组栈尾追加元素的方式有两种

  2. $a = []; array_push($a,'test');
  3. $a[] = 'test';
复制代码
那么这两种方式有什么区别呢?
我们先来比较一下性能
ArrayPush
  1. 一个 ArrayPush 类

  2. pushEachOne() 循环体中使用 array_push() 来为 $a 追加元素
  3. pushEachTwo() 循环体中使用 $a[] = $var 来为 $a 追加元素
复制代码
  1. /**

  2. * Class ArrayPush

  3. */

  4. class ArrayPush

  5. {



  6.     /**

  7.      * @param int $times

  8.      * @return array

  9.      */

  10.     public static function pushEachOne(int $times): array

  11.     {

  12.         $a = [];

  13.         $b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  14.         for ($i = 0; $i < $times; $i++) {

  15.             array_push($a, $b[$i % 10]);

  16.         }

  17.         return $a;

  18.     }



  19.     /**

  20.      * @param int $times

  21.      * @return array

  22.      */

  23.     public static function pushEachTwo(int $times): array

  24.     {

  25.         $a = [];

  26.         $b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  27.         for ($i = 0; $i < $times; $i++) {

  28.             $a[] = $b[$i % 10];

  29.         }

  30.         return $a;

  31.     }



  32. }
复制代码
编写代码测试
循环追加 100 万个元素
  1. ini_set('memory_limit', '4000M');

  2. $timeOne = microtime(true);

  3. $a       = ArrayPush::pushEachOne(1000000);

  4. echo 'count pushEachOne result | ' . count($a) . PHP_EOL;

  5. $timeTwo = microtime(true);

  6. $b       = ArrayPush::pushEachTwo(1000000);

  7. echo 'count pushEachTwo result | ' . count($b) . PHP_EOL;

  8. $timeThree = microtime(true);

  9. echo PHP_EOL;

  10. echo 'pushEachOne | ' . ($timeTwo - $timeOne) . PHP_EOL;

  11. echo 'pushEachTwo | ' . ($timeThree - $timeTwo) . PHP_EOL;

  12. echo PHP_EOL;
复制代码
结果
结果不言而喻,$a[] = 比使用 array_push() 快了接近三倍
  1. count pushEachOne result | 1000000

  2. count pushEachTwo result | 1000000



  3. pushEachOne | 1.757071018219

  4. pushEachTwo | 0.67165303230286
复制代码
分析
array_push()为什么慢?这么慢,我们还有使用它的场景吗?
官方手册
array_push — 将一个或多个单元压入数组的末尾(入栈)
array_push ( array &$array , mixed $value1 [, mixed $... ] ) : int
array_push() 将 array 当成一个栈,并将传入的变量压入 array 的末尾。array 的长度将根据入栈变量的数目增加。和如下效果相同:
  1. <?php$array[] = $var;?>
复制代码
并对每个传入的值重复以上动作。
Note: 如果用 array_push() 来给数组增加一个单元,还不如用 \$array[] = ,因为这样没有调用函数的额外负担。
Note: 如果第一个参数不是数组,array_push() 将发出一条警告。这和 \$var[] 的行为不同,后者会新建一个数组。
官方源码
看一下源码中的 array_push()
  1. /* {{{ proto int array_push(array stack, mixed var [, mixed ...])

  2.    Pushes elements onto the end of the array */

  3. PHP_FUNCTION(array_push)

  4. {

  5.     zval   *args,       /* Function arguments array */

  6.            *stack,      /* Input array */

  7.             new_var;    /* Variable to be pushed */

  8.     int i,              /* Loop counter */

  9.         argc;           /* Number of function arguments */



  10.     //这一段是函数的参数解析

  11.     ZEND_PARSE_PARAMETERS_START(2, -1)

  12.         Z_PARAM_ARRAY_EX(stack, 0, 1)

  13.         Z_PARAM_VARIADIC('+', args, argc)

  14.     ZEND_PARSE_PARAMETERS_END();



  15.     /* For each subsequent argument, make it a reference, increase refcount, and add it to the end of the array */

  16.     for (i = 0; i < argc; i++) {

  17.         //拷贝一个

  18.         ZVAL_COPY(&new_var, &args[i]);



  19.         //插入新数值,自动

  20.         if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var) == NULL) {

  21.             if (Z_REFCOUNTED(new_var)) Z_DELREF(new_var);

  22.             php_error_docref(NULL, E_WARNING, "Cannot add element to the array as the next element is already occupied");

  23.             RETURN_FALSE;

  24.         }

  25.     }



  26.     /* Clean up and return the number of values in the stack */

  27.     RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));

  28. }

  29. /* }}} */
复制代码
$a[] = 的实现是根据赋值的变量类型调用了一系列 Zend_API 函数 add_next_index_* ,它们在设置一个对应类型的 zval 值以后直接调用了 zend_hash_next_index_insert
  1. ZEND_API int add_next_index_long(zval *arg, zend_long n) /* {{{ */

  2. {

  3.     zval tmp;



  4.     ZVAL_LONG(&tmp, n);

  5.     return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;

  6. }

  7. /* }}} */



  8. ZEND_API int add_next_index_null(zval *arg) /* {{{ */

  9. {

  10.     zval tmp;



  11.     ZVAL_NULL(&tmp);

  12.     return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;

  13. }

  14. /* }}} */



  15. ZEND_API int add_next_index_bool(zval *arg, int b) /* {{{ */

  16. {

  17.     zval tmp;



  18.     ZVAL_BOOL(&tmp, b);

  19.     return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;

  20. }

  21. /* }}} */



  22. ZEND_API int add_next_index_resource(zval *arg, zend_resource *r) /* {{{ */

  23. {

  24.     zval tmp;



  25.     ZVAL_RES(&tmp, r);

  26.     return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;

  27. }

  28. /* }}} */



  29. ZEND_API int add_next_index_double(zval *arg, double d) /* {{{ */

  30. {

  31.     zval tmp;



  32.     ZVAL_DOUBLE(&tmp, d);

  33.     return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;

  34. }

  35. /* }}} */



  36. ZEND_API int add_next_index_str(zval *arg, zend_string *str) /* {{{ */

  37. {

  38.     zval tmp;



  39.     ZVAL_STR(&tmp, str);

  40.     return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;

  41. }

  42. /* }}} */



  43. ZEND_API int add_next_index_string(zval *arg, const char *str) /* {{{ */

  44. {

  45.     zval tmp;



  46.     ZVAL_STRING(&tmp, str);

  47.     return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;

  48. }

  49. /* }}} */



  50. ZEND_API int add_next_index_stringl(zval *arg, const char *str, size_t length) /* {{{ */

  51. {

  52.     zval tmp;



  53.     ZVAL_STRINGL(&tmp, str, length);

  54.     return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;

  55. }

  56. /* }}} */



  57. ZEND_API int add_next_index_zval(zval *arg, zval *value) /* {{{ */

  58. {

  59.     return zend_hash_next_index_insert(Z_ARRVAL_P(arg), value) ? SUCCESS : FAILURE;

  60. }

  61. /* }}} */
复制代码
总结
经过上面的分析,仿佛 array_push() 没有任何存在的意义,真的是这样吗?
  • 一般情况下,array_push() 性能太差,所以我们应当使用 $array[] = 来替换掉它
  • 如果一次追加多个单元,使用 array_push()



分享到 :
0 人收藏

3 个回复

倒序浏览
阿新  金牌会员 | 2019-11-15 07:46:22
呵呵,低调,低调!
Zachary_Fan  高级会员 | 2019-12-8 10:30:36
我只是路过的......
wolfy  高级会员 | 2019-12-15 05:01:35
楼下的接上
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

举报|Archiver|手机版|小黑屋|Lvchakeji Inc.  

Powered by Discuz! X3.3 © 2001-2016 Comsenz Inc.

返回顶部