STL 的 hashtable 分析(四)

#-hashtable #-STL

erase 函数用来删除 hash 表中关键值为 __key 的所有节点。首先调用 _M_bkt_num_key 函数计算出关键值为 __key 的节点处在以 _M_buckets 中的第 __n 个元素为链表头的链表中,将该链表头暂存在 __first 中,如果 __first 不为空,遍历整个链表先删除除 __first 之外的所有关键值为 __key 的节点,并记录删除的节点个数。最后检测 __first 指向的节点的关键值是否为 __key ,如果是,则删除该节点。最后返回删除的节点个数。

template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
typename hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>::size_type 
hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>::erase(const key_type& __key)
{
  const size_type __n = _M_bkt_num_key(__key);
  _Node* __first = _M_buckets[__n];
  size_type __erased = 0;

  if (__first) {
    _Node* __cur = __first;
    _Node* __next = __cur->_M_next;
    while (__next) {
      if (_M_equals(_M_get_key(__next->_M_val), __key)) {
	__cur->_M_next = __next->_M_next;
	_M_delete_node(__next);
	__next = __cur->_M_next;
	++__erased;
	--_M_num_elements;
      }
      else {
	__cur = __next;
	__next = __cur->_M_next;
      }
    }
    if (_M_equals(_M_get_key(__first->_M_val), __key)) {
      _M_buckets[__n] = __first->_M_next;
      _M_delete_node(__first);
      ++__erased;
      --_M_num_elements;
    }
  }
  return __erased;
}

erase 函数删除指定位置 __it 的节点。如果函数指定的位置是一个有效位置,则函数首先计算出指定位置的节点处在以 _M_buckets 的第 __n 个元素为链表头的链表中,并获取到这一链表头。遍历该链表找到函数所指定的位置,然后删除指定位置的节点,这里如果函数指定的位置在链表头,则需要更新链表头。

template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
void hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>::erase(const iterator& __it)
{
  _Node* __p = __it._M_cur;
  if (__p) {
    const size_type __n = _M_bkt_num(__p->_M_val);
    _Node* __cur = _M_buckets[__n];

    if (__cur == __p) {
      _M_buckets[__n] = __cur->_M_next;
      _M_delete_node(__cur);
      --_M_num_elements;
    }
    else {
      _Node* __next = __cur->_M_next;
      while (__next) {
	if (__next == __p) {
	  __cur->_M_next = __next->_M_next;
	  _M_delete_node(__next);
	  --_M_num_elements;
	  break;
	}
	else {
	  __cur = __next;
	  __next = __cur->_M_next;
	}
      }
    }
  }
}

_M_erase_bucket 函数用来删除以 _M_buckets[__n] 为链表头中的链表中 __first 和 __last 限定的范围的节点。函数首先获取到链表头,并暂存在指针 __cur 中,如果 __first 等于 __cur(链表头),则调用 _M_erase_bucket 的另一个定义删除从链表头到 __last 位置(不包括 __last)的所有节点。否则遍历链表,直到找到 __first 指定的位置。然后删除从该位置到 __last(不包括 __last) 位置的节点。

template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
void hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>
  ::_M_erase_bucket(const size_type __n, _Node* __first, _Node* __last)
{
  _Node* __cur = _M_buckets[__n];
  if (__cur == __first)
    _M_erase_bucket(__n, __last);
  else {
    _Node* __next;
    for (__next = __cur->_M_next; 
	 __next != __first; 
	 __cur = __next, __next = __cur->_M_next)
      ;
    while (__next != __last) {
      __cur->_M_next = __next->_M_next;
      _M_delete_node(__next);
      __next = __cur->_M_next;
      --_M_num_elements;
    }
  }
}

函数 erase 删除以 _M_buckets[__n] 为链表头的链表中,从链表头开始到 __last 位置(不包括 __last) 的所有节点。

template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
void hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>
  ::_M_erase_bucket(const size_type __n, _Node* __last)
{
  _Node* __cur = _M_buckets[__n];
  while (__cur != __last) {
    _Node* __next = __cur->_M_next;
    _M_delete_node(__cur);
    __cur = __next;
    _M_buckets[__n] = __cur;
    --_M_num_elements;
  }
}

函数 erase 删除迭代器 __first 和迭代器 __last 限定的区域的节点。

函数首先获取 __first 指向的位置的节点在以 _M_buckets 的第 __f_bucket 个元素为链表头的链表中。同时获取到 __last 指向的位置的节点在以 _M_buckets 的第 __l_bucket 个元素为链表头的链表中。

如果 __first 指向的是无效位置,则 _f_bucket 为 _M_buckets.size(),如果 __last 指向的是无效位置,则 __l_bucket 为 _M_buckets.size() 。如果 __first 和 __last 指向的是同一个位置,则直接退出。

否则查看 __f_bucket 和 __l_bucket 是否相同,如果相同,则说明 __first 和 __last 指向的位置在同一条链表中,直接调用 _M_erase_bucket 函数删除 __first 到 __last 之间的节点。否则如果 __f_bucket 和 __l_bucket 不相等,则首先删除以 _M_buckets[__f_bucket] 为链表头的链表中从 __fisrt 开始的所有节点。然后 __n 从 __f_bucket + 1 开始到 __l_bucket 结束,删除以 _M_buckets[__n] 为链表头的链表中的所有节点。同时如果 __l_buckets 不为 _M_buckets.size() ,则删除以 _M_buckets[__l_bucket] 为链表头的链表中从链表头开始到 __last (不包括 __last) 位置结束的所有节点。

template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
void hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>
  ::erase(iterator __first, iterator __last)
{
  size_type __f_bucket = __first._M_cur ? 
    _M_bkt_num(__first._M_cur->_M_val) : _M_buckets.size();
  size_type __l_bucket = __last._M_cur ? 
    _M_bkt_num(__last._M_cur->_M_val) : _M_buckets.size();

  if (__first._M_cur == __last._M_cur)
    return;
  else if (__f_bucket == __l_bucket)
    _M_erase_bucket(__f_bucket, __first._M_cur, __last._M_cur);
  else {
    _M_erase_bucket(__f_bucket, __first._M_cur, 0);
    for (size_type __n = __f_bucket + 1; __n < __l_bucket; ++__n)
      _M_erase_bucket(__n, 0);
    if (__l_bucket != _M_buckets.size())
      _M_erase_bucket(__l_bucket, __last._M_cur);
  }
}

resize 函数要求 _M_buckets 中元素个数至少为指定的 __num_elements_hint 个。

而且要求 _M_buckets.size() 为 __stl_prime_list 中的预定义的素数。函数首先获取到当前 _M_buckets 中元素个数 __old_n 。如果 __num_elements_hint 超出了 __old_n(当前 _M_buckets 中的元素个数)。则调用 _M_next_size 函数获取到__stl_prime_list 中大于等于 _num_elements_hint 的最小素数。

如果所有的素数都比 __num_elements_hint 小,则返回最大的素数。用 __n 暂存 _M_next_size 返回的素数。如果 __n 仍然大于 __old_n(因为 __n 可能会比 __num_elements_hint 小) 。则重新定义一个 vector<_Node*, _All> 类型的变量 __tmp ,并为 __tmp 初始化 __n 个为空指针的元素。

然后将当前 _M_buckets 中的元素移动到 __tmp 中,因为 _M_buckets 和 __tmp 中的元素个数不同,所以具有相同节点值的节点在 _M_buckets 和 __tmp 中位置可能是不相同的。对于 _M_buckets 中的每个元素首先计算出它应处在以 __tmp 的第 __new_bucket 个元素为链表头的链表中,然后将该节点插入到以 __tmp[__new_bucket] 为链表头的链表的头部。

在 _M_buckets 中的所有元素都移动到 __tmp 中之后,交换 _M_buckets 和 __tmp 。这样 tmp 中的内容交换到 __M_buckets 中,而 _M_buckets 中的内容交换到 __tmp 中,但 __tmp 离开了作用于就会调用析构函数删除从 _M_buckets 中交换而来的内容。

template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
void hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>
  ::resize(size_type __num_elements_hint)
{
  const size_type __old_n = _M_buckets.size();
  if (__num_elements_hint > __old_n) {
    const size_type __n = _M_next_size(__num_elements_hint);
    if (__n > __old_n) {
      vector<_Node*, _All> __tmp(__n, (_Node*)(0),
				 _M_buckets.get_allocator());
      __STL_TRY {
	for (size_type __bucket = 0; __bucket < __old_n; ++__bucket) {
	  _Node* __first = _M_buckets[__bucket];
	  while (__first) {
	    size_type __new_bucket = _M_bkt_num(__first->_M_val, __n);
	    _M_buckets[__bucket] = __first->_M_next;
	    __first->_M_next = __tmp[__new_bucket];
	    __tmp[__new_bucket] = __first;
	    __first = _M_buckets[__bucket];          
	  }
	}
	_M_buckets.swap(__tmp);
      }
#         ifdef __STL_USE_EXCEPTIONS
      catch(...) {
	for (size_type __bucket = 0; __bucket < __tmp.size(); ++__bucket) {
	  while (__tmp[__bucket]) {
	    _Node* __next = __tmp[__bucket]->_M_next;
	    _M_delete_node(__tmp[__bucket]);
	    __tmp[__bucket] = __next;
	  }
	}
	throw;
      }
#         endif /* __STL_USE_EXCEPTIONS */
    }
  }
}

clear 函数删除 hash 表中的所有节点,并释放这些节点所占用的空间。

template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
void hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>::clear()
{
  for (size_type __i = 0; __i < _M_buckets.size(); ++__i) {
    _Node* __cur = _M_buckets[__i];
    while (__cur != 0) {
      _Node* __next = __cur->_M_next;
      _M_delete_node(__cur);
      __cur = __next;
    }
    _M_buckets[__i] = 0;
  }
  _M_num_elements = 0;
}

函数 _M_copy_from 用来将给定 hash 表 __ht 中的节点复制到当前 hash 表中。函数首先将当前 hash 表清空,然后为 _M_buckets 分配能够容纳 __ht 中元素的空间。然后为当前 _M_buckets 初始化 __ht._M_buckets.size() 个为空指针的元素。再将 __ht 中对应位置的元素复制到当前 hash 表中。

template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
void hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>
  ::_M_copy_from(const hashtable& __ht)
{
  _M_buckets.clear();
  _M_buckets.reserve(__ht._M_buckets.size());
  _M_buckets.insert(_M_buckets.end(), __ht._M_buckets.size(), (_Node*) 0);
  __STL_TRY {
    for (size_type __i = 0; __i < __ht._M_buckets.size(); ++__i) {
      const _Node* __cur = __ht._M_buckets[__i];
      if (__cur) {
	_Node* __copy = _M_new_node(__cur->_M_val);
	_M_buckets[__i] = __copy;

	for (_Node* __next = __cur->_M_next; 
	     __next; 
	     __cur = __next, __next = __cur->_M_next) {
	  __copy->_M_next = _M_new_node(__next->_M_val);
	  __copy = __copy->_M_next;
	}
      }
    }
    _M_num_elements = __ht._M_num_elements;
  }
  __STL_UNWIND(clear());
}

函数 operator== 用来判断给定的两个 hash 表是否相等,首先要求两个 hash 表中 _M_buckets 的元素个数相同,其次要求以 _M_buckets 中元素为链表头的链表中的对应节点要相同。如果所有节点都一一对应、个个相同,则认为这两个 hash 是相同的。否则认为他们不相同。

template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
bool operator==(const hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>& __ht1,
		const hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>& __ht2)
{
  typedef typename hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>::_Node _Node;
  if (__ht1._M_buckets.size() != __ht2._M_buckets.size())
    return false;
  for (int __n = 0; __n < __ht1._M_buckets.size(); ++__n) {
    _Node* __cur1 = __ht1._M_buckets[__n];
    _Node* __cur2 = __ht2._M_buckets[__n];
    for ( ; __cur1 && __cur2 && __cur1->_M_val == __cur2->_M_val;
	  __cur1 = __cur1->_M_next, __cur2 = __cur2->_M_next)
      {}
    if (__cur1 || __cur2)
      return false;
  }
  return true;
}

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