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

【虚拟资源买卖网www.cnused.com】源码虚拟资源交易平台,免费开通VIP商户!
吆喝资源网官方客服QQ:787749153官方唯一QQ号码
文字广告位招租      好位置不等人!200元/月

标题是不要在循环体中使用 array_push(),其实这只是本篇文章的结论之一
下面我们一起研究一下 php 语言中数组的追加元素

向数组追加元素

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

  • $a = []; array_push($a,'test');
  • $a[] = 'test';

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

我们先来比较一下性能

ArrayPush

一个 ArrayPush

  • pushEachOne() 循环体中使用 array_push() 来为 $a 追加元素
  • pushEachTwo() 循环体中使用 $a[] = $var 来为 $a 追加元素
/** * Class ArrayPush */
class ArrayPush
{ /** * @param int $times * @return array */ public static function pushEachOne(int $times): array { $a = []; $b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; for ($i = 0; $i < $times; $i++) { array_push($a, $b[$i % 10]); } return $a; } /** * @param int $times * @return array */ public static function pushEachTwo(int $times): array { $a = []; $b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; for ($i = 0; $i < $times; $i++) { $a[] = $b[$i % 10]; } return $a; }
}

编写代码测试

循环追加 100 万个元素

ini_set('memory_limit', '4000M');
$timeOne = microtime(true);
$a = ArrayPush::pushEachOne(1000000);
echo 'count pushEachOne result | ' . count($a) . PHP_EOL;
$timeTwo = microtime(true);
$b = ArrayPush::pushEachTwo(1000000);
echo 'count pushEachTwo result | ' . count($b) . PHP_EOL;
$timeThree = microtime(true);
echo PHP_EOL;
echo 'pushEachOne | ' . ($timeTwo - $timeOne) . PHP_EOL;
echo 'pushEachTwo | ' . ($timeThree - $timeTwo) . PHP_EOL;
echo PHP_EOL;

结果

结果不言而喻,$a[] = 比使用 array_push() 快了接近三倍

count pushEachOne result | 1000000
count pushEachTwo result | 1000000
pushEachOne | 1.757071018219
pushEachTwo | 0.67165303230286

分析

array_push()为什么慢?这么慢,我们还有使用它的场景吗?

官方手册

array_push — 将一个或多个单元压入数组的末尾(入栈)

array_push ( array &$array , mixed $value1 [, mixed $... ] ) : int

array_push()array 当成一个栈,并将传入的变量压入 array 的末尾。array 的长度将根据入栈变量的数目增加。和如下效果相同:

<?php$array[] = $var;?>

并对每个传入的值重复以上动作。

Note: 如果用 array_push() 来给数组增加一个单元,还不如用 /$array[] = ,因为这样没有调用函数的额外负担。

Note: 如果第一个参数不是数组,array_push() 将发出一条警告。这和 /$var[] 的行为不同,后者会新建一个数组。

官方源码

看一下源码中的 array_push()

/* {{{ proto int array_push(array stack, mixed var [, mixed ...]) Pushes elements onto the end of the array */
PHP_FUNCTION(array_push)
{ zval *args, /* Function arguments array */ *stack, /* Input array */ new_var; /* Variable to be pushed */ int i, /* Loop counter */ argc; /* Number of function arguments */ //这一段是函数的参数解析 ZEND_PARSE_PARAMETERS_START(2, -1) Z_PARAM_ARRAY_EX(stack, 0, 1) Z_PARAM_VARIADIC('+', args, argc) ZEND_PARSE_PARAMETERS_END(); /* For each subsequent argument, make it a reference, increase refcount, and add it to the end of the array */ for (i = 0; i < argc; i++) { //拷贝一个 ZVAL_COPY(&new_var, &args[i]); //插入新数值,自动 if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var) == NULL) { if (Z_REFCOUNTED(new_var)) Z_DELREF(new_var); php_error_docref(NULL, E_WARNING, "Cannot add element to the array as the next element is already occupied"); RETURN_FALSE; } } /* Clean up and return the number of values in the stack */ RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
}
/* }}} */

$a[] = 的实现是根据赋值的变量类型调用了一系列 Zend_API 函数 add_next_index_* ,它们在设置一个对应类型的 zval 值以后直接调用了 zend_hash_next_index_insert

ZEND_API int add_next_index_long(zval *arg, zend_long n) /* {{{ */
{ zval tmp; ZVAL_LONG(&tmp, n); return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;
}
/* }}} */
ZEND_API int add_next_index_null(zval *arg) /* {{{ */
{ zval tmp; ZVAL_NULL(&tmp); return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;
}
/* }}} */
ZEND_API int add_next_index_bool(zval *arg, int b) /* {{{ */
{ zval tmp; ZVAL_BOOL(&tmp, b); return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;
}
/* }}} */
ZEND_API int add_next_index_resource(zval *arg, zend_resource *r) /* {{{ */
{ zval tmp; ZVAL_RES(&tmp, r); return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;
}
/* }}} */
ZEND_API int add_next_index_double(zval *arg, double d) /* {{{ */
{ zval tmp; ZVAL_DOUBLE(&tmp, d); return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;
}
/* }}} */
ZEND_API int add_next_index_str(zval *arg, zend_string *str) /* {{{ */
{ zval tmp; ZVAL_STR(&tmp, str); return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;
}
/* }}} */
ZEND_API int add_next_index_string(zval *arg, const char *str) /* {{{ */
{ zval tmp; ZVAL_STRING(&tmp, str); return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;
}
/* }}} */
ZEND_API int add_next_index_stringl(zval *arg, const char *str, size_t length) /* {{{ */
{ zval tmp; ZVAL_STRINGL(&tmp, str, length); return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;
}
/* }}} */
ZEND_API int add_next_index_zval(zval *arg, zval *value) /* {{{ */
{ return zend_hash_next_index_insert(Z_ARRVAL_P(arg), value) ? SUCCESS : FAILURE;
}
/* }}} */

总结

经过上面的分析,仿佛 array_push() 没有任何存在的意义,真的是这样吗?

  • 一般情况下,array_push() 性能太差,所以我们应当使用 $array[] = 来替换掉它
  • 如果一次追加多个单元,使用 array_push()

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

本文转自PHP中文网,吆喝网(http://yaoohe.com)搜集整理,如有疑问请联系本站客服!

本文由 吆喝资源网 作者:shijian 发表,其版权均为 吆喝资源网 所有,文章内容系作者个人观点,不代表 吆喝资源网 对观点赞同或支持。如需转载,请注明文章来源。

1

发表评论