详细四种函数如何利用就不多先容了,我想看我文章的都不是LV1级别的吧,如果不懂请自行查阅php.net网站。
测试方法随机天生一个大数组,然后任取一些元素分别调用上面的几个函数判断是否在天生的大数组内,记录下运行时花费的最大内存和实行韶光。
测试环境PHP 7.2.34 (cli) (built: Mar 14 2021 18:35:27) ( NTS )Copyright (c) 1997-2018 The PHP GroupZend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies with Zend OPcache v7.2.34, Copyright (c) 1999-2018, by Zend Technologie
测试代码
下面函数创建随机字符的数组:
function random_str( $cnt = 1000, $length = 8 ) { $chars = array(39;a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y','Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '@', '#', '$', '%', '^', '&', '', '(', ')', '-', '_', '[', ']', '{', '}', '<', '>', '~', '`', '+', '=', ',', '.', ';', ':', '/', '?', '|'); $a = []; for($j = 0; $j < $cnt; ++$j) { $keys = array_rand($chars, $length); $p = ''; for($i = 0; $i < $length; $i++) { $p .= $chars[$keys[$i]]; } $a[] = $p; } return $a;}
最大内存利用情形:
function get_memory() {$size = memory_get_peak_usage(true); $mod = 1024; $units = array('B', 'Kb', 'Mb', 'Gb'); $format = '%.2f%s'; for ($i = 0; $size > $mod; $i++) { $size /= $mod; } return sprintf($format, round($size, 3), $units[$i]);}
详细的测试代码大致是这样:
// 创建大小为10w的数组$arr = random_str(100000);$cnt = count($arr);// 取1个出来判断$search_arr = [];for ($i=0; $i<1; ++$i) { $r = rand(0, $cnt-1); $search_arr[] = $arr[$r];}echo "----------- in_array -----------" . PHP_EOL;$t0 = microtime(true);for ($i=0; $i < 1; ++$i) { if (in_array($search_arr[$i], $arr)) { echo ""; }}echo "memory: " . get_memory() . PHP_EOL;echo "time elapsed: " . (microtime(true) - $t0) . PHP_EOL;
上面是创建10w大小数组,取一个用in_array()来判断,同样地,其他的几个函数,把in_array()改一下就行,但是要把稳的是:
isset()和array_key_exists()利用数组的key来判断,以是须要多一步:将创建的数组的key和value用array_flip()对调一下,这一步也是要加入到统计中去的。
代码如下:
echo "----------- array_key_exists -----------" . PHP_EOL;$t4 = microtime(true);$key_arr = array_flip($arr);echo "array_flip time elapsed : " . (microtime(true) - $t4) . PHP_EOL;for ($i=0; $i<1; ++$i) { if (array_key_exists($search_arr[$i], $key_arr)) { echo "" ; }}echo "memory: " . get_memory() . PHP_EOL;echo "time elapsed: " . (microtime(true) - $t4) . PHP_EOL;
isset()函数也是类似。
测试结果如下:
从图中可以看到,in_array和array_search遥遥领先于其余两个,当100w数据时,前两个的实行速率快了两个数量级。内存方面,isset和array_key_exists也是内存花费大户,花费的内存险些是in_array和array_search的1.5倍。
仔细剖析可以创造isset和array_key_exists实在并不慢,罪魁罪魁是array_flip(),由于这两个函数都是通过key来判断的,以是我们须要把value转换成key,这就额外须要存储100w数据的空间,并且还要遍历把数据插入进去,可想而知是很耗内存和韶光的。可以看如下截图:
全体实行韶光0.14150094985962,而array_flip()这个函数实行就花了0.1414098739624,isset和array_key_exists本身是很快的。
如果要判断多个元素呢?是不是会降落array_flip()函数带来的影响?我们连续测试。
测试100个元素:
内存险些没有变革,但实行韶光in_array和array_search比后两个慢了几倍,变革得很明显,而isset和array_key_exists实行韶光和查询2个元素险些同等,查询的个数并不影响其效率,相反,in_array和array_search的实行效率和个数成正比。
再来测试1000个:
在查询1000个的情形下,我等in_array和array_search这两个函数的结果已经不耐烦了,而isset和array_key_exists仍旧很淡定,变革不大。
上面是对大字符串数组的测试,轻微做下调度来看看如果是连续的数字会是什么结果。
结果差不多,同样是查找1000个,array_key_exists()函数花费了0.028817176818848,in_array()函数仍旧用了1.4303638935089。
为什么那么为什么基于key的查询速率会快这么多呢?本着“不抛弃,不放弃”的原则,我查了下PHP中关于数组的实现,利用了HashTable。
想必学校老师都已经教过吧(【算法与数据构造】复习起来),查找时通过hash函数打算元素索引,这个过程是很快的可以看作是常量O(c)的韶光繁芜度,以是就有了上面的测试结果,in_array()和array_search()在查找时须要遍历全体数组,韶光繁芜度是O(n),这当然慢咯,而通过key查找只要实行hash函数定位到索引,这速率肯定不是一个量级的。当然,鱼和熊掌不能兼得,通过key查找首先须要布局这样的数组,势必会降落效率还要花费很多内存。
总结在数据量少的情形下,建议利用in_array和array_search速率够快而且内存占用少,数据量特殊大的时候,可以用isset和array_key_exists,同时也要考虑下内存的占用。
参考https://stackoverflow.com/questions/2473989/list-of-big-o-for-php-functions/2484455#2484455
https://en.wikipedia.org/wiki/Hash_table#Separate_chaining