STL 的 hashtable 分析(二)

#-hashtable #-STL

函数 insert_unique 将节点值为 __obj 的新节点插入到 hash 表,但要求新插入的节点的关键值不能和已存在的节点相同,否则插入失败,如果新插入的节点的关键值不与 hash 表中的其他节点相同则返回有指向新插入的节点的迭代器和 true 构造而成的pair 类型的变量,否则返回由 end() 和 false 构造而成的 pair 类型的变量。

函数中首先调用 resize 检测 _M_buckets 中是否有大于或者等于 _M_num_elements + 1 个的元素。如果有,resize 函数不作任何改动,否则函数会重新分配空间,并复制 hash 表中的节点到新的空间。然后调用 insert_unique_noresize 函数将节点值为 __obj 的新节点插入到 hash 表中(插入成功与否取决于新节点的关键值是否存在与 hash 表中的某个已有节点中)

  pair<iterator, bool> insert_unique(const value_type& __obj)
  {
    resize(_M_num_elements + 1);
    return insert_unique_noresize(__obj);
  }

函数 insert_equal 将节点值为 __obj 的新节点插入到 hash 表中,函数允许新节点的关键值存在于 hash 表中的某个已有节点中。 函数也是先调用 resize 函数分配合适的空间,然后调用 insert_equaL_noresize 函数将节点值为 __obj 的新节点插入到 hash 表中。

  iterator insert_equal(const value_type& __obj)
  {
    resize(_M_num_elements + 1);
    return insert_equal_noresize(__obj);
  }

insert_unique 函数用来将 __f 到 __l 之间的内容插入到当前 hash 表中,并且要求新节点插入之后,hash 表中各个节点的关键值各不相同。具体的操作通过 insert_unique 的另外的定义来实现。

  template <class _InputIterator>
  void insert_unique(_InputIterator __f, _InputIterator __l)
  {
    insert_unique(__f, __l, __ITERATOR_CATEGORY(__f));
  }

如果迭代器是 input_iterator 类型,则逐个调用 insert_unique 插入 __f 到 __l 之间的元素。

  template <class _InputIterator>
  void insert_unique(_InputIterator __f, _InputIterator __l,
		     input_iterator_tag)
  {
    for ( ; __f != __l; ++__f)
      insert_unique(*__f);
  }

如果迭代器类型是 forward_iterator 类型,则先计算 __f 和 __l 之间的差值,然后调用 resize 函数以保证 _M_buckets 中有足够的空间容纳即将被插入的元素。然后逐个调用 insert_unique_noresize 函数将 __f 和 __l 之间的内容插入到 _M_buckets 中。

  template <class _ForwardIterator>
  void insert_unique(_ForwardIterator __f, _ForwardIterator __l,
		     forward_iterator_tag)
  {
    size_type __n = 0;
    distance(__f, __l, __n);
    resize(_M_num_elements + __n);
    for ( ; __n > 0; --__n, ++__f)
      insert_unique_noresize(*__f);
  }

insert_equal 函数用来将 __f 到 __l 之间的内容插入到当前 hash 表中。并且允许 hash 表中不同的节点带有相同的关键值。

  template <class _InputIterator>
  void insert_equal(_InputIterator __f, _InputIterator __l)
  {
    insert_equal(__f, __l, __ITERATOR_CATEGORY(__f));
  }

如果迭代器类型为 input_iterator,则逐个调用 insert_equal 函数的另一个定义将 __f 到 __l 中的内容插入到当前 hash 表中。

  template <class _InputIterator>
  void insert_equal(_InputIterator __f, _InputIterator __l,
		    input_iterator_tag)
  {
    for ( ; __f != __l; ++__f)
      insert_equal(*__f);
  }

如果迭代器类型为 forward_iterator ,则先计算出 __f 到 __l 的差值,然后调用 resize 以保证 _M_buckets 中有足够的元素能容纳即将插入的新节点。然后调用 insert_equal_noresize 函数逐个将 __f 到 __l 之间的内容插入到当前 hash 表中。

  template <class _ForwardIterator>
  void insert_equal(_ForwardIterator __f, _ForwardIterator __l,
		    forward_iterator_tag)
  {
    size_type __n = 0;
    distance(__f, __l, __n);
    resize(_M_num_elements + __n);
    for ( ; __n > 0; --__n, ++__f)
      insert_equal_noresize(*__f);
  }

find 函数用来查找 hash 表中关键值为 __k 的节点,如果存在,则返回指向该节点的迭代器,否则返回一个指向无效位置的迭代器(和 end() 相等)。函数首先调用 _M_bkt_num_key 计算出关键值为 __key 的节点应该处在以 _M_buckets 中的第 __n 个元素为链表头的链表中。

然后逐个检测以 _M_buckets 的第 __n 个元素为链表头的链表中的元素,如果碰到关键值为 __key 的节点,则返回指向该节点的迭代器,如果遍历了整个链表都不能找到关键值为 __key 的元素,则返回一个指向无效位置的迭代器。

  iterator find(const key_type& __key) 
  {
    size_type __n = _M_bkt_num_key(__key);
    _Node* __first;
    for ( __first = _M_buckets[__n];
	  __first && !_M_equals(_M_get_key(__first->_M_val), __key);
	  __first = __first->_M_next)
      {}
    return iterator(__first, this);
  } 

count 函数用来查找 hash 表中关键值为 __key 的节点个数。函数首先计算出关键值为 __key 的节点应该处在以 _M_buckets 的第 __n 个元素为链表头的链表中,然后遍历该链表,计算出其中关键值为 __key 的节点个数。并将其作为返回值返回。

  size_type count(const key_type& __key) const
  {
    const size_type __n = _M_bkt_num_key(__key);
    size_type __result = 0;

    for (const _Node* __cur = _M_buckets[__n]; __cur; __cur = __cur->_M_next)
      if (_M_equals(_M_get_key(__cur->_M_val), __key))
	++__result;
    return __result;
  }

函数 find_or_insert 用来将数据值为 __obj 的节点插入到 hash 表中,函数首先调用 resize 函数以保证 _M_buckets 中有足够的元素来容纳新节点。然后调用 _M_bkt_num 计算出节点值为 __obj 的节点处在以 _M_buckets 中第 __n 个元素为链表头的链表中。然后遍历该链表,查找节点值为 __obj 的节点的关键值是否已存在于链表中的某个节点中,如果有,则直接返回。如果没有,则将新节点插入到表的表头位置,让 _M_buckets[__n] 指向这个新节点。

template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
typename hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>::reference 
hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>::find_or_insert(const value_type& __obj)
{
  resize(_M_num_elements + 1);

  size_type __n = _M_bkt_num(__obj);
  _Node* __first = _M_buckets[__n];

  for (_Node* __cur = __first; __cur; __cur = __cur->_M_next)
    if (_M_equals(_M_get_key(__cur->_M_val), _M_get_key(__obj)))
      return __cur->_M_val;

  _Node* __tmp = _M_new_node(__obj);
  __tmp->_M_next = __first;
  _M_buckets[__n] = __tmp;
  ++_M_num_elements;
  return __tmp->_M_val;
}

函数 equal_range 用来查找关键值为指定值 __key 的节点所在的区域,这个区域由两个迭代器限定。将由这两个迭代器组成的 pair 类型的变量作为函数的返回值。

函数首先计算出关键值为 __key 的节点处在以 _M_buckets 的第 __n 个元素为链表头的链表中。从该链表的表头开始遍历,查找到第一个关键值为 __key 的节点 __first。然后从 __first 的下一个节点开始继续向后遍历,直到碰到一个节点值不为 __key 的节点 __cur (因为关键值为 __key 的节点是连续存储的,所以可以认为 __first 和 __cur 之间的节点就是所有关键值为 __key 的节点)。然后将由 __first 和 __cur 组成的迭代器组合作为返回值返回。

但如果直到链表的最后一个元素都没有找到关键值不为 __key 的节点,则可以认为该链表中从 __first 开始的所有元素的关键值都为 __key 。则需将一个新的链表的链表头作为结束标记,这个链表头可以通过从 _M_buckets 的第 __n + 1 个元素开始,查找到第一个不为空的元素。则该元素指向的节点就可以作为结束标记,它被认为是 hash 表中以 _M_buckets 的第 __n 个元素作为链表头的链表中的最后一个节点的下一个节点。

如果遍历了以 _M_buckets 的第 __n 个元素为链表头的链表都没有找到关键值为 __key 的元素,则返回两个由 end() 组合而成的返回值。

template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
pair<typename hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>::const_iterator, 
     typename hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>::const_iterator> 
hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>
  ::equal_range(const key_type& __key) const
{
  typedef pair<const_iterator, const_iterator> _Pii;
  const size_type __n = _M_bkt_num_key(__key);

  for (const _Node* __first = _M_buckets[__n] ;
       __first; 
       __first = __first->_M_next) {
    if (_M_equals(_M_get_key(__first->_M_val), __key)) {
      for (const _Node* __cur = __first->_M_next;
	   __cur;
	   __cur = __cur->_M_next)
	if (!_M_equals(_M_get_key(__cur->_M_val), __key))
	  return _Pii(const_iterator(__first, this),
		      const_iterator(__cur, this));
      for (size_type __m = __n + 1; __m < _M_buckets.size(); ++__m)
	if (_M_buckets[__m])
	  return _Pii(const_iterator(__first, this),
		      const_iterator(_M_buckets[__m], this));
      return _Pii(const_iterator(__first, this), end());
    }
  }
  return _Pii(end(), end());
}

STL 的 hashtable 分析(一)</br> STL 的 hashtable 分析(二)</br> STL 的 hashtable 分析(三)</br> STL 的 hashtable 分析(四)</br>