国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - PHP教程 - PHP中array_keys和array_unique函數源碼的分析

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

2020-12-22 18:16PHP教程網 PHP教程

本文從array_keys和array_unique的源碼分析出函數的性能,并給出了優化建議,十分不錯的文章,有需要的小伙伴可以參考下

性能分析

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$test=array();
for($run=0; $run<10000; $run++)
$test[]=rand(0,100);
 
$time=microtime(true);
 
$out = array_unique($test);
 
$time=microtime(true)-$time;
echo 'Array Unique: '.$time."\n";
 
$time=microtime(true);
 
$out=array_keys(array_flip($test));
 
$time=microtime(true)-$time;
echo 'Keys Flip: '.$time."\n";
 
$time=microtime(true);
 
$out=array_flip(array_flip($test));
 
$time=microtime(true)-$time;
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/* {{{ proto array array_keys(array input [, mixed search_value[, bool strict]])
  Return just the keys from the input array, optionally only for the specified       search_value */
PHP_FUNCTION(array_keys)
{
  //變量定義
  zval *input,        /* Input array */
     *search_value = NULL,  /* Value to search for */
     **entry,        /* An entry in the input array */
      res,          /* Result of comparison */
     *new_val;        /* New value */
  int  add_key;        /* Flag to indicate whether a key should be added */
  char *string_key;      /* String key */
  uint  string_key_len;
  ulong num_key;        /* Numeric key */
  zend_bool strict = 0;    /* do strict comparison */
  HashPosition pos;
  int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;
 
  //程序解析參數
  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|zb", &input, &search_value, &strict) == FAILURE) {
    return;
  }
 
  // 如果strict是true,則設置is_equal_func為is_identical_function,即全等比較
  if (strict) {
    is_equal_func = is_identical_function;
  }
 
  /* 根據search_vale初始化返回的數組大小 */
  if (search_value != NULL) {
    array_init(return_value);
  } else {
    array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
  }
  add_key = 1;
 
  /* 遍歷輸入的數組參數,然后添加鍵值到返回的數組 */
  zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);//重置指針
  //循環遍歷數組
  while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) {
    // 如果search_value不為空
    if (search_value != NULL) {
      // 判斷search_value與當前的值是否相同,并將比較結果保存到add_key變量
      is_equal_func(&res, search_value, *entry TSRMLS_CC);
      add_key = zval_is_true(&res);
    }
 
    if (add_key) {
      // 創建一個zval結構體
      MAKE_STD_ZVAL(new_val);
 
      // 根據鍵值是字符串還是整型數字將值插入到return_value中
      switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 1, &pos)) {
        case HASH_KEY_IS_STRING:
          ZVAL_STRINGL(new_val, string_key, string_key_len - 1, 0);
          // 此函數負責將值插入到return_value中,如果鍵值已存在,則使用新值更新對應的值,否則直接插入
          zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL);
          break;
 
        case HASH_KEY_IS_LONG:
          Z_TYPE_P(new_val) = IS_LONG;
          Z_LVAL_P(new_val) = num_key;
          zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL);
          break;
      }
    }
 
    // 移動到下一個
    zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
  }
}
/* }}} */

以上是array_keys函數底層的源碼。為方便理解,筆者添加了一些中文注釋。如果需要查看原始代碼,可以點擊查看。這個函數的功能就是新建一個臨時數組,然后將鍵值對重新復制到新的數組,如果復制過程中有重復的鍵值出現,那么就用新的值替換。這個函數的主要步驟是地57和63行調用的zend_hash_next_index_insert函數。該函數將元素插入到數組中,如果出現重復的值,則使用新的值更新原鍵值指向的值,否則直接插入,時間復雜度是O(n)。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/* {{{ proto array array_flip(array input)
  Return array with key <-> value flipped */
PHP_FUNCTION(array_flip)
{
  // 定義變量
  zval *array, **entry, *data;
  char *string_key;
  uint str_key_len;
  ulong num_key;
  HashPosition pos;
 
  // 解析數組參數
  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
    return;
  }
 
  // 初始化返回數組
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
 
  // 重置指針
  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) {
    // 初始化一個結構體
    MAKE_STD_ZVAL(data);
    // 將原數組的值賦值為新數組的鍵
    switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &str_key_len, &num_key, 1, &pos)) {
      case HASH_KEY_IS_STRING:
        ZVAL_STRINGL(data, string_key, str_key_len - 1, 0);
        break;
      case HASH_KEY_IS_LONG:
        Z_TYPE_P(data) = IS_LONG;
        Z_LVAL_P(data) = num_key;
        break;
    }
 
    // 將原數組的鍵賦值為新數組的值,如果有重復的,則使用新值覆蓋舊值
    if (Z_TYPE_PP(entry) == IS_LONG) {
      zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &data, sizeof(data), NULL);
    } else if (Z_TYPE_PP(entry) == IS_STRING) {
      zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &data, sizeof(data), NULL);
    } else {
      zval_ptr_dtor(&data); /* will free also zval structure */
      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only flip STRING and INTEGER values!");
    }
 
    // 下一個
    zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
  }
}
/* }}} */

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/* {{{ proto array array_unique(array input [, int sort_flags])
  Removes duplicate values from array */
PHP_FUNCTION(array_unique)
{
  // 定義變量
  zval *array, *tmp;
  Bucket *p;
  struct bucketindex {
    Bucket *b;
    unsigned int i;
  };
  struct bucketindex *arTmp, *cmpdata, *lastkept;
  unsigned int i;
  long sort_type = PHP_SORT_STRING;
 
  // 解析參數
  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
    return;
  }
 
  // 設置比較函數
  php_set_compare_func(sort_type TSRMLS_CC);
 
  // 初始化返回數組
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
  // 將值拷貝到新數組
  zend_hash_copy(Z_ARRVAL_P(return_value), Z_ARRVAL_P(array), (copy_ctor_func_t) zval_add_ref, (void *)&tmp, sizeof(zval*));
 
  if (Z_ARRVAL_P(array)->nNumOfElements <= 1) {  /* 什么都不做 */
    return;
  }
 
  /* 根據target_hash buckets的指針創建數組并排序 */
  arTmp = (struct bucketindex *) pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), Z_ARRVAL_P(array)->persistent);
  if (!arTmp) {
    zval_dtor(return_value);
    RETURN_FALSE;
  }
  for (i = 0, p = Z_ARRVAL_P(array)->pListHead; p; i++, p = p->pListNext) {
    arTmp[i].b = p;
    arTmp[i].i = i;
  }
  arTmp[i].b = NULL;
  // 排序
  zend_qsort((void *) arTmp, i, sizeof(struct bucketindex), php_array_data_compare TSRMLS_CC);
 
  /* 遍歷排序好的數組,然后刪除重復的元素 */
  lastkept = arTmp;
  for (cmpdata = arTmp + 1; cmpdata->b; cmpdata++) {
    if (php_array_data_compare(lastkept, cmpdata TSRMLS_CC)) {
      lastkept = cmpdata;
    } else {
      if (lastkept->i > cmpdata->i) {
        p = lastkept->b;
        lastkept = cmpdata;
      } else {
        p = cmpdata->b;
      }
      if (p->nKeyLength == 0) {
        zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
      } else {
        if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) {
          zend_delete_global_variable(p->arKey, p->nKeyLength - 1 TSRMLS_CC);
        } else {
          zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h);
        }
      }
    }
  }
  pefree(arTmp, Z_ARRVAL_P(array)->persistent);
}
/* }}} */

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

結論

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

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 国产剧情一区 | 国产中文字幕一区 | 91精品国产一区二区三区 | 五月婷婷在线观看 | 成人在线小视频 | 免费黄色小视频 | 国产精品第一国产精品 | 午夜精品久久久久久久久久久久久 | 成人av免费 | 亚洲电影在线播放 | 黄色一级视频 | 亚洲精品国产第一综合99久久 | 欧美一区二区在线免费观看 | 中文字幕综合在线 | 欧美日一区二区 | 国产欧美精品区一区二区三区 | 毛片特级 | 成人综合区 | 日韩在线精品视频 | 日韩精品一区二区三区第95 | 综合激情网 | 国产成人精品免费视频大全最热 | 久久久久久亚洲 | 精品免费国产一区二区三区 | 美日韩成人 | 亚洲一区二区高清 | 亚洲国产精品自拍 | 国产精品久久久久久久午夜 | 久久久成人网 | 国产精品日韩在线观看 | 久久国产综合 | 精品国产不卡一区二区三区 | 国产欧美综合视频 | 中文字幕 在线观看 | 国产精品69毛片高清亚洲 | 精品视频网站 | 欧美成人精品一区二区 | 国产一区中文字幕 | 久草电影在线观看 | 国产免费黄色 | 亚洲福利 |