原文連結
標題是不要在迴圈體中使用
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$...
] ) : intarray_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()