对于大数组查找某个值in_array和array_flip+isset的性能比较

  •   
  • 3325
  • PHP
  • 10
  • super_dodo
  • 2016/09/21

假设有一个10万条记录的数组,需要查找某一个值是否存在该数组内.大多数情况下直接使用in_array这个PHP的自带函数,但是当这个数组很大和很复杂的时候,建议使用array_flip和isset的方式来提高性能.

首先来看一下PHP源码,从代码中可以看出while循环在遍历数组中的所有元素取值做对比,如果数组中元素特别多又频繁调用的时候,效率可想而知。

由于数组的键是放在Hash表的key中的,Hash遍历会根据键的哈希值去查找,效率非常高,所以尝试了一上array_flip将键值对换,然后用isset去判断,效率呈指数级提高。

//得到毫秒的方法
function getMillisecond() {
	list($t1, $t2) = explode(' ', microtime());
	return (float)sprintf('%.0f',(floatval($t1)+floatval($t2))*1000);
}

//构建一个10万条自增的新数组
$array = [];
for ($i = 0; $i < 100000; $i++) {
	$array[] = $i;
}

//直接使用in_array的方法去循环执行10万次查找
$start_time = getMillisecond();
for ($i = 0; $i < 100000; $i++) {
	$k = rand(0,100000);
	if(in_array($k,$array)){
		echo $k;
	}
}
echo '使用in_array占用时间:', (getMillisecond() - $start_time) . "毫秒<hr>";


//反转键和值array_flip
$start_time = getMillisecond();
$array_flip = array_flip($array);
for ($i = 0; $i < 100000; $i++) {
	$k = rand(0,100000);
	if(isset($array_flip[$k])){
		echo $k;
	}
}
echo '使用isset占用时间:', (getMillisecond() - $start_time) . "毫秒";


//输出结果
//使用in_array占用时间:6336毫秒
//使用isset占用时间:121毫秒

//使用in_array占用时间:6347毫秒
//使用isset占用时间:362毫秒

//使用in_array占用时间:6374毫秒
//使用isset占用时间:161毫秒

//对于10万条数据执行10万次的效率来说,结果令人吃惊,所以建议在做统计等密集运算的时候,尽量不要使用in_array函数!

附上PHP的in_array的源码,来源于网络,经供参考.

/* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
 * 0 = return boolean
 * 1 = return key
 */
static void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
{
	zval *value,                /* value to check for */
		 *array,                /* array to check in */
		 **entry,               /* pointer to array entry */
		  res;                  /* comparison result */
	HashPosition pos;           /* hash iterator */
	zend_bool strict = 0;       /* strict comparison or not */
	int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "za|b", &value, &array, &strict) == FAILURE) {
		return;
	}

	if (strict) {
		is_equal_func = is_identical_function;
	}

	zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
	while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) {
		is_equal_func(&res, value, *entry TSRMLS_CC);
		if (Z_LVAL(res)) {
			if (behavior == 0) {
				RETURN_TRUE;
			} else {
				zend_hash_get_current_key_zval_ex(Z_ARRVAL_P(array), return_value, &pos);
				return;
			}
		}
		zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
	}

	RETURN_FALSE;
}
/* }}} */