亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 語言 > PHP > 正文

PHP中array_keys和array_unique函數源碼的分析

2024-09-04 11:46:45
字體:
來源:轉載
供稿:網友

性能分析:從運行性能上分析,看看下面的測試代碼:

  1. $test=array(); 
  2.  
  3. for($run=0; $run<10000; $run++) 
  4.  
  5. $test[]=rand(0,100); 
  6.  
  7. $time=microtime(true); 
  8.  
  9. $out = array_unique($test); 
  10.  
  11. $time=microtime(true)-$time
  12.  
  13. echo 'Array Unique: '.$time."/n"
  14.  
  15. $time=microtime(true); 
  16.  
  17. $out=array_keys(array_flip($test)); 
  18.  
  19. $time=microtime(true)-$time
  20.  
  21. echo 'Keys Flip: '.$time."/n"
  22.  
  23. $time=microtime(true); 
  24.  
  25. $out=array_flip(array_flip($test)); 
  26.  
  27. $time=microtime(true)-$time
  28.  
  29. echo 'Flip Flip: '.$time."/n"

運行結果如下:

從上圖可以看到,使用array_unique函數需要0.069s;使用array_flip后再使用array_keys函數需要0.00152s;使用兩次array_flip函數需要0.00146s。

測試結果表明,使用array_flip后再調用array_keys函數比array_unique函數快。那么,具體原因是什么呢?讓我們看看在PHP底層,這兩個函數是怎么實現的。

源碼分析:

  1. /* {{{ proto array array_keys(array input [, mixed search_value[, bool strict]]) 
  2.  
  3.   Return just the keys from the input array, optionally only for the specified       search_value */ 
  4.  
  5. PHP_FUNCTION(array_keys
  6.  
  7.  
  8.   //變量定義 
  9.  
  10.   zval *input,        /* Input array */ 
  11.  
  12.      *search_value = NULL,  /* Value to search for */ 
  13.  
  14.      **entry,        /* An entry in the input array */ 
  15.  
  16.       res,          /* Result of comparison */ 
  17.  
  18.      *new_val;        /* New value */ 
  19.  
  20.   int  add_key;        /* Flag to indicate whether a key should be added */ 
  21.  
  22.   char *string_key;      /* String key */ 
  23.  
  24.   uint  string_key_len; 
  25.  
  26.   ulong num_key;        /* Numeric key */ 
  27.  
  28.   zend_bool strict = 0;    /* do strict comparison */ 
  29.  
  30.   HashPosition pos; 
  31.  
  32.   int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function; 
  33.  
  34.   
  35.  
  36.   //程序解析參數 
  37.  
  38.   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|zb", &input, &search_value, &strict) == FAILURE) { 
  39.  
  40.     return
  41.  
  42.   } 
  43.  
  44.   
  45.  
  46.   // 如果strict是true,則設置is_equal_func為is_identical_function,即全等比較 
  47.  
  48.   if (strict) { 
  49.  
  50.     is_equal_func = is_identical_function; 
  51.  
  52.   } 
  53.  
  54.   
  55.  
  56.   /* 根據search_vale初始化返回的數組大小 */ 
  57.  
  58.   if (search_value != NULL) { 
  59.  
  60.     array_init(return_value); 
  61.  
  62.   } else { 
  63.  
  64.     array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input))); 
  65.  
  66.   } 
  67.  
  68.   add_key = 1; 
  69.  
  70.   
  71.  
  72.   /* 遍歷輸入的數組參數,然后添加鍵值到返回的數組 */ 
  73.  
  74.   zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);//重置指針 
  75.  
  76.   //循環遍歷數組 
  77.  
  78.   while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) { 
  79.  
  80.     // 如果search_value不為空 
  81.  
  82.     if (search_value != NULL) { 
  83.  
  84.       // 判斷search_value與當前的值是否相同,并將比較結果保存到add_key變量 
  85.  
  86.       is_equal_func(&res, search_value, *entry TSRMLS_CC); 
  87.  
  88.       add_key = zval_is_true(&res); 
  89.  
  90.     } 
  91.  
  92.   
  93.  
  94.     if (add_key) { 
  95.  
  96.       // 創建一個zval結構體 
  97.  
  98.       MAKE_STD_ZVAL(new_val); 
  99.  
  100.   
  101.  
  102.       // 根據鍵值是字符串還是整型數字將值插入到return_value中 
  103.  
  104.       switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 1, &pos)) { 
  105.  
  106.         case HASH_KEY_IS_STRING: 
  107.  
  108.           ZVAL_STRINGL(new_val, string_key, string_key_len - 1, 0); 
  109.  
  110.           // 此函數負責將值插入到return_value中,如果鍵值已存在,則使用新值更新對應的值,否則直接插入 
  111.  
  112.           zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL); 
  113.  
  114.           break
  115.  
  116.   
  117.  
  118.         case HASH_KEY_IS_LONG: 
  119.  
  120.           Z_TYPE_P(new_val) = IS_LONG
  121.  
  122.           Z_LVAL_P(new_val) = num_key; 
  123.  
  124.           zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL); 
  125.  
  126.           break
  127.  
  128.       } 
  129.  
  130.     } 
  131.  
  132.  //Vevb.com 
  133.  
  134.     // 移動到下一個 
  135.  
  136.     zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos); 
  137.  
  138.   } 
  139.  
  140.  
  141. /* }}} */ 

以上是array_keys函數底層的源碼。為方便理解,筆者添加了一些中文注釋。如果需要查看原始代碼,可以點擊查看。這個函數的功能就是新建一個臨時數組,然后將鍵值對重新復制到新的數組,如果復制過程中有重復的鍵值出現,那么就用新的值替換。

這個函數的主要步驟是地57和63行調用的zend_hash_next_index_insert函數。該函數將元素插入到數組中,如果出現重復的值,則使用新的值更新原鍵值指向的值,否則直接插入,時間復雜度是O(n)。

  1. /* {{{ proto array array_flip(array input) 
  2.  
  3.   Return array with key <-> value flipped */ 
  4.  
  5. PHP_FUNCTION(array_flip
  6.  
  7.  
  8.   // 定義變量 
  9.  
  10.   zval *array, **entry, *data; 
  11.  
  12.   char *string_key; 
  13.  
  14.   uint str_key_len; 
  15.  
  16.   ulong num_key; 
  17.  
  18.   HashPosition pos;  
  19.  
  20.   // 解析數組參數 
  21.  
  22.   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) { 
  23.  
  24.     return
  25.  
  26.   }
  27.   
  28.  
  29.   // 初始化返回數組 
  30.  
  31.   array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array))); 
  32.  
  33.   // 重置指針 
  34.  
  35.   zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos); 
  36.  
  37.   // 遍歷每個元素,并執行鍵<->值交換操作 
  38.  
  39.   while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) { 
  40.  
  41.     // 初始化一個結構體 
  42.  
  43.     MAKE_STD_ZVAL(data); 
  44.  
  45.     // 將原數組的值賦值為新數組的鍵 
  46.  
  47.     switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &str_key_len, &num_key, 1, &pos)) { 
  48.  
  49.       case HASH_KEY_IS_STRING: 
  50.  
  51.         ZVAL_STRINGL(data, string_key, str_key_len - 1, 0); 
  52.  
  53.         break
  54.  
  55.       case HASH_KEY_IS_LONG: 
  56.  
  57.         Z_TYPE_P(data) = IS_LONG
  58.  
  59.         Z_LVAL_P(data) = num_key; 
  60.  
  61.         break
  62.  
  63.     }  
  64.     // 將原數組的鍵賦值為新數組的值,如果有重復的,則使用新值覆蓋舊值 
  65.  
  66.     if (Z_TYPE_PP(entry) == IS_LONG) { 
  67.  
  68.       zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &data, sizeof(data), NULL); 
  69.  
  70.     } else if (Z_TYPE_PP(entry) == IS_STRING) { 
  71.  
  72.       zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &data, sizeof(data), NULL); 
  73.  
  74.     } else { 
  75.  
  76.       zval_ptr_dtor(&data); /* will free also zval structure */ 
  77.  
  78.       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only flip STRING and INTEGER values!"); 
  79.  
  80.     } 
  81.  
  82.     // 下一個 
  83.  
  84.     zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos); 
  85.  
  86.   } 
  87.  
  88.  
  89. /* }}} */ 

上面就是是array_flip函數的源碼。點擊鏈接查看原始代碼。這個函數主要的做的事情就是創建一個新的數組,遍歷原數組。在26行開始將原數組的值賦值為新數組的鍵,然后在37行開始將原數組的鍵賦值為新數組的值,如果有重復的,則使用新值覆蓋舊值。整個函數的時間復雜度也是O(n)。因此,使用了array_flip之后再使用array_keys的時間復雜度是O(n)。

接下來,我們看看array_unique函數的源碼。點擊鏈接查看原始代碼。

  1. /* {{{ proto array array_unique(array input [, int sort_flags]) 
  2.  
  3.   Removes duplicate values from array */ 
  4.  
  5. PHP_FUNCTION(array_unique
  6.  
  7.  
  8.   // 定義變量 
  9.  
  10.   zval *array, *tmp; 
  11.  
  12.   Bucket *p; 
  13.  
  14.   struct bucketindex { 
  15.  
  16.     Bucket *b; 
  17.  
  18.     unsigned int i; 
  19.  
  20.   }; 
  21.  
  22.   struct bucketindex *arTmp, *cmpdata, *lastkept; 
  23.  
  24.   unsigned int i; 
  25.  
  26.   long sort_type = PHP_SORT_STRING; 
  27.  
  28.   
  29.  
  30.   // 解析參數 
  31.  
  32.   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) { 
  33.  
  34.     return
  35.  
  36.   } 
  37.  
  38.   
  39.  
  40.   // 設置比較函數 
  41.  
  42.   php_set_compare_func(sort_type TSRMLS_CC); 
  43.  
  44.   
  45.  
  46.   // 初始化返回數組 
  47.  
  48.   array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array))); 
  49.  
  50.   // 將值拷貝到新數組 
  51.  
  52.   zend_hash_copy(Z_ARRVAL_P(return_value), Z_ARRVAL_P(array), (copy_ctor_func_t) zval_add_ref, (void *)&tmp, sizeof(zval*)); 
  53.  
  54.   
  55.  
  56.   if (Z_ARRVAL_P(array)->nNumOfElements <= 1) {  /* 什么都不做 */ 
  57.  
  58.     return
  59.  
  60.   } 
  61.  
  62.   
  63.  
  64.   /* 根據target_hash buckets的指針創建數組并排序 */ 
  65.  
  66.   arTmp = (struct bucketindex *) pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), Z_ARRVAL_P(array)->persistent); 
  67.  
  68.   if (!arTmp) { 
  69.  
  70.     zval_dtor(return_value); 
  71.  
  72.     RETURN_FALSE; 
  73.  
  74.   } 
  75.  
  76.   for (i = 0, p = Z_ARRVAL_P(array)->pListHead; p; i++, p = p->pListNext) { 
  77.  
  78.     arTmp[i].b = p; 
  79.  
  80.     arTmp[i].i = i; 
  81.  
  82.   } 
  83.  
  84.   arTmp[i].b = NULL; 
  85.  
  86.   // 排序 
  87.  
  88.   zend_qsort((void *) arTmp, i, sizeof(struct bucketindex), php_array_data_compare TSRMLS_CC); 
  89.  
  90.   
  91.  
  92.   /* 遍歷排序好的數組,然后刪除重復的元素 */ 
  93.  
  94.   lastkept = arTmp; 
  95.  
  96.   for (cmpdata = arTmp + 1; cmpdata->b; cmpdata++) { 
  97.  
  98.     if (php_array_data_compare(lastkept, cmpdata TSRMLS_CC)) { 
  99.  
  100.       lastkept = cmpdata; 
  101.  
  102.     } else { 
  103.  
  104.       if (lastkept->i > cmpdata->i) { 
  105.  
  106.         p = lastkept->b; 
  107.  
  108.         lastkept = cmpdata; 
  109.  
  110.       } else { 
  111.  
  112.         p = cmpdata->b; 
  113.  
  114.       } 
  115.  
  116.       if (p->nKeyLength == 0) { 
  117.  
  118.         zend_hash_index_del(Z_ARRVAL_P(return_value), p->h); 
  119.  
  120.       } else { 
  121.  
  122.         if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) { 
  123.  
  124.           zend_delete_global_variable(p->arKey, p->nKeyLength - 1 TSRMLS_CC); 
  125.  
  126.         } else { 
  127.  
  128.           zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h); 
  129.  
  130.         } 
  131.  
  132.       } 
  133.  
  134.     } 
  135.  
  136.   } 
  137.  
  138.   pefree(arTmp, Z_ARRVAL_P(array)->persistent); 
  139.  
  140.  
  141. /* }}} */ 

可以看到,這個函數初始化一個新的數組,然后將值拷貝到新數組,然后在45行調用排序函數對數組進行排序,排序的算法是zend引擎的塊樹排序算法。接著遍歷排序好的數組,刪除重復的元素。整個函數開銷最大的地方就在調用排序函數上,而快排的時間復雜度是O(nlogn),因此,該函數的時間復雜度是O(nlogn)。

結論:

因為array_unique底層調用了快排算法,加大了函數運行的時間開銷,導致整個函數的運行較慢。這就是為什么array_keys比array_unique函數更快的原因。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
中文字幕日韩高清| 亚洲黄色在线观看| 精品高清一区二区三区| 国产精品99久久久久久久久| 亚洲色图18p| 亚洲无亚洲人成网站77777| 久久综合伊人77777蜜臀| 91精品国产综合久久男男| 久久久久中文字幕2018| 日韩av在线免费| 亚洲男人7777| 国产精品久久久久久久久男| 亚洲无限av看| 欧美日韩国产成人高清视频| 国产精品九九久久久久久久| 亚洲欧美中文另类| 亚洲成人在线视频播放| 国产欧美精品在线播放| 日韩av在线网页| 国产精品91久久| 国产99视频精品免视看7| 久久综合88中文色鬼| 亚洲美女精品久久| 中文字幕av一区二区三区谷原希美| 国内伊人久久久久久网站视频| 欧美激情伊人电影| 岛国视频午夜一区免费在线观看| 欧美极品少妇xxxxⅹ免费视频| 亚洲国产欧美日韩精品| 色av吧综合网| 国产成人午夜视频网址| 欧美精品18videos性欧美| 国产精品亚洲欧美导航| 欧美一级片在线播放| 国产在线拍偷自揄拍精品| 亚洲第一视频在线观看| 色婷婷亚洲mv天堂mv在影片| 97精品免费视频| 在线视频欧美日韩精品| 亚洲在线视频观看| 国产精品美腿一区在线看| 日韩av电影中文字幕| 青青在线视频一区二区三区| 国产欧美 在线欧美| 国产成人在线视频| 欧美另类老女人| 中文字幕av一区二区三区谷原希美| 国产精品一区=区| 法国裸体一区二区| 亚洲第一综合天堂另类专| 久久精品福利视频| 69av成年福利视频| 欧美日韩aaaa| 亚洲综合色av| 97色在线播放视频| 88国产精品欧美一区二区三区| 青青草99啪国产免费| 亚洲欧美日韩在线高清直播| 国产精品精品久久久| 菠萝蜜影院一区二区免费| 欧美国产第二页| 中文字幕精品一区久久久久| 欧美激情综合亚洲一二区| 国产欧美最新羞羞视频在线观看| 国产精品视频26uuu| 国产精品久久久久久av福利| 欧美黑人一区二区三区| 国产亚洲欧美日韩精品| 永久免费精品影视网站| 欧美午夜宅男影院在线观看| 亚洲专区在线视频| 2025国产精品视频| 成人免费淫片aa视频免费| 欧美视频精品一区| 一区二区欧美日韩视频| 日韩中文在线中文网三级| 国产成人精品a视频一区www| 国产精品人成电影在线观看| 国内精品国产三级国产在线专| 日韩美女视频中文字幕| 亚洲精品美女在线观看| 亚洲xxx自由成熟| 亚洲美女又黄又爽在线观看| 欧美三级xxx| 亚洲福利视频专区| 久久精品99国产精品酒店日本| 91欧美日韩一区| 久久综合88中文色鬼| 亚洲女在线观看| 2018国产精品视频| 亚洲欧美中文日韩在线| 欧美一区二区三区……| 日韩av免费观影| 91深夜福利视频| 日韩在线观看免费全| 久久精品中文字幕免费mv| 日韩视频中文字幕| 国产精品亚洲视频在线观看| 2024亚洲男人天堂| 97超级碰在线看视频免费在线看| 日本不卡免费高清视频| 国产日韩精品视频| 亚洲免费视频在线观看| 亚洲国语精品自产拍在线观看| 91在线视频成人| 九九热这里只有精品免费看| 欧美在线视频一区二区| 精品福利一区二区| 国产一区二区美女视频| 亚洲欧美www| 欧美人与性动交| 人妖精品videosex性欧美| 欧美专区第一页| 久久久最新网址| 国产精品毛片a∨一区二区三区|国| 日韩av电影免费观看高清| 色偷偷91综合久久噜噜| 亚洲美女性生活视频| 日本精品视频网站| 国产视频精品va久久久久久| 国产精品国产福利国产秒拍| 国产精品扒开腿做爽爽爽男男| 欧美午夜精品久久久久久人妖| 亚洲免费精彩视频| 国产精品成av人在线视午夜片| 疯狂做受xxxx高潮欧美日本| 在线电影欧美日韩一区二区私密| 亚洲欧洲日韩国产| 久99九色视频在线观看| 国产亚洲精品一区二555| 亚洲欧美日韩视频一区| 不卡在线观看电视剧完整版| 日韩av在线免费| 欧美精品激情视频| 精品国产欧美一区二区五十路| 日韩国产在线看| 国产aaa精品| 久久福利视频网| 亚洲第一偷拍网| 亚洲三级免费看| 伊人久久久久久久久久| 欧美一区亚洲一区| 亚洲欧美精品一区| 亚洲成人黄色网| 亚州国产精品久久久| 国产精品av免费在线观看| 国产精品免费一区| 日本一区二区在线播放| 日韩禁在线播放| 亚洲精品电影网站| 久久精品在线视频| 精品久久久久久久久久ntr影视| 97在线看福利| 国产精品久久久av| 国产精品偷伦一区二区| 视频一区视频二区国产精品| 成人乱色短篇合集| 日韩久久免费电影| www.午夜精品| 亚洲free嫩bbb| 国产精品丝袜高跟| 日韩成人在线视频网站| 国产一区二中文字幕在线看| 国产亚洲精品美女|