标题是不要在循环体中使用 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 | 1000000count pushEachTwo result | 1000000pushEachOne | 1.757071018219pushEachTwo | 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()

更多PHP相关技术文章,请访问PHP教程栏目进行学习!

更多相关文章

  1. 分享几个有意思的数组方法
  2. 在PHP中处理JSON数组以及对象
  3. 使用PHP多维数组重组方法,兼容API接口
  4. php数组去重的方法
  5. php数组实现原理
  6. php对象转数组的函数
  7. php数组转json
  8. php数组打乱顺序
  9. php判断数组某个值是否存在

随机推荐

  1. Android中的Parcel机制 实现Bundle传递对
  2. Cordova与Android之一 Activity跳转
  3. Android系统启动流程(2) —— 解析Zygote
  4. Android中 GridLayout 【网格布局】 全面
  5. Android延长Toast的时间以及自定义Toast
  6. Android深入浅出系列课程---Lesson2 AAF1
  7. React-Native测试报告
  8. AsyncTask的使用详解
  9. android:gravity与android:layout_gravit
  10. android UI 配置参数