当前位置:Gxlcms > PHP教程 > 不要在循环体中使用 array_push ()

不要在循环体中使用 array_push ()

时间:2021-07-01 10:21:17 帮助过:65人阅读

标题是不要在循环体中使用 array_push(),其实这只是本篇文章的结论之一

下面我们一起研究一下 php 语言中数组的追加元素

向数组追加元素

我们知道 php 在数组栈尾追加元素的方式有两种

  1. $a = []; array_push($a,'test');
  2. $a[] = 'test';

那么这两种方式有什么区别呢?

我们先来比较一下性能

  1. ArrayPush
  2. 一个 ArrayPush 类
  3. pushEachOne() 循环体中使用 array_push() 来为 $a 追加元素
  4. pushEachTwo() 循环体中使用 $a[] = $var 来为 $a 追加元素
  5. /**
  6. * Class ArrayPush
  7. */
  8. class ArrayPush
  9. {
  10. /**
  11. * @param int $times
  12. * @return array
  13. */
  14. public static function pushEachOne(int $times): array
  15. {
  16. $a = [];
  17. $b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  18. for ($i = 0; $i < $times; $i++) {
  19. array_push($a, $b[$i % 10]);
  20. }
  21. return $a;
  22. }
  23. /**
  24. * @param int $times
  25. * @return array
  26. */
  27. public static function pushEachTwo(int $times): array
  28. {
  29. $a = [];
  30. $b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  31. for ($i = 0; $i < $times; $i++) {
  32. $a[] = $b[$i % 10];
  33. }
  34. return $a;
  35. }
  36. }

编写代码测试

循环追加 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 () 为什么慢?这么慢,我们还有使用它的场景吗?

官方手册

  1. array_push — 将一个或多个单元压入数组的末尾(入栈)
  2. array_push ( array &$array , mixed $value1 [, mixed $... ] ) : int
  3. array_push() 将 array 当成一个栈,并将传入的变量压入 array 的末尾。array 的长度将根据入栈变量的数目增加。和如下效果相同:
  4. <?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()

以上就是不要在循环体中使用 array_push ()的详细内容,更多请关注Gxlcms其它相关文章!

人气教程排行