【问题标题】:STL hash table resizeSTL 哈希表调整大小
【发布时间】:2013-03-31 07:56:40
【问题描述】:

SGI STL使用分离链来实现哈希表。它包含一个桶向量,每个元素都是桶列表的第一个节点链接。有时,如果插入哈希表的元素太多,我们必须调整向量的大小。但是我们什么时候应该调整向量的大小呢?SGI STL 是这样做的:将新的元素数量与旧向量的大小进行比较。如果前者较大,则调整向量的大小。向量的元素只是桶列表的第一个节点链接!但是桶链接列表可以包含很多元素,为什么不考虑这个?

以下是SGI STL源码(来自enter link description here):

template <class _Val, class _Key, class _HashFcn,
          class _ExtractKey, class _EqualKey, class _Alloc>
class hashtable {
public:
  typedef _Key key_type;
  typedef _Val value_type;
  typedef _HashFcn hasher;
  typedef _EqualKey key_equal;

  typedef size_t            size_type;
  typedef ptrdiff_t         difference_type;
  typedef value_type*       pointer;
  typedef const value_type* const_pointer;
  typedef value_type&       reference;
  typedef const value_type& const_reference;

  hasher hash_funct() const { return _M_hash; }
  key_equal key_eq() const { return _M_equals; }

private:
  typedef _Hashtable_node<_Val> _Node;

#ifdef __STL_USE_STD_ALLOCATORS
public:
  typedef typename _Alloc_traits<_Val,_Alloc>::allocator_type allocator_type;
  allocator_type get_allocator() const { return _M_node_allocator; }
private:
  typename _Alloc_traits<_Node, _Alloc>::allocator_type _M_node_allocator;
  _Node* _M_get_node() { return _M_node_allocator.allocate(1); }
  void _M_put_node(_Node* __p) { _M_node_allocator.deallocate(__p, 1); }
# define __HASH_ALLOC_INIT(__a) _M_node_allocator(__a), 
#else /* __STL_USE_STD_ALLOCATORS */
public:
  typedef _Alloc allocator_type;
  allocator_type get_allocator() const { return allocator_type(); }
private:
  typedef simple_alloc<_Node, _Alloc> _M_node_allocator_type;
  _Node* _M_get_node() { return _M_node_allocator_type::allocate(1); }
  void _M_put_node(_Node* __p) { _M_node_allocator_type::deallocate(__p, 1); }
# define __HASH_ALLOC_INIT(__a)
#endif /* __STL_USE_STD_ALLOCATORS */

private:
  hasher                _M_hash;
  key_equal             _M_equals;
  _ExtractKey           _M_get_key;
  vector<_Node*,_Alloc> _M_buckets;//Here!!!!!
  size_type             _M_num_elements;

请查看私有元素_M_buckets。

以及SGI STL的向量resize代码:

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];//Here!!
          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);
      }

以及桶向量的易用性:

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;
    }
  }
}

我们可以看到向量的元素是桶链表的第一个节点链接!

我的问题是,为什么 SGI STL 会这样?谢谢!

【问题讨论】:

  • 由于现有向量不足以容纳所有新元素,表明需要重定位才能容纳新元素。
  • @AlokSave vector的元素只是bucket list的第一个节点link!!!但是linklist可以包含很多元素,为什么不考虑这个?
  • 你指的是什么类? hash_map,hash_set,unordered_map,unordered_set,hash_table?哪个版本?请 (a) 显示代码或 (b) 链接到相关文档页面
  • @sehe 我已经发布了 SGI STL 的源代码,谢谢!

标签: c++ algorithm stl


【解决方案1】:

vector的元素只是桶列表的第一个节点链接!

向量的元素,显然不是“只有桶列表的第一个节点链接”。

取而代之的是桶向量而不是桶列表。

如果您想要一个存储桶列表,请使用 std::list 作为存储桶。

【讨论】:

  • 无需重新发布您的所有 cmets :) 此外,更少的感叹号会更友好。欢迎来到 SO
【解决方案2】:

std::vector 如果插入元素的数量超过其容量,则自动重新分配。这对于std::vector 整体而言是正确的,而不仅仅是在这种情况下。

【讨论】:

  • vector的元素只是bucket list的第一个节点link!!!但是linklist可以包含很多元素,为什么不考虑这个呢?
猜你喜欢
  • 2011-06-24
  • 2013-12-21
  • 2012-10-14
  • 2021-03-14
  • 2014-04-21
  • 1970-01-01
  • 1970-01-01
  • 2017-06-07
相关资源
最近更新 更多