填充unordered_set的更有效方法?

时间:2019-04-12 13:23:45

标签: c++ visual-c++ stl unordered-set

我有一个连续存储在内存中的整数数组,我想将它们全部添加到unordered_set集合中。

现在,我一次添加一个。

for (int i = 0; i < count; i++)
    collection.insert(pi[i]);

有什么方法可以更有效地做到这一点?

我意识到这些项不是连续存储在集合中的,因此不会像将数组移交给集合那样简单。但这可以以某种方式进行优化吗?

2 个答案:

答案 0 :(得分:6)

unordered_set具有一个构造函数,该构造函数采用一系列元素来初始添加它们:

template< class InputIt >
unordered_set( InputIt first, InputIt last,
           size_type bucket_count = /*implementation-defined*/,
           const Hash& hash = Hash(),
           const key_equal& equal = key_equal(),
           const Allocator& alloc = Allocator() );

因此,您只需执行collection = std::unordered_set{ p, p + count };并留待实施即可。

正如其他用户在评论中指出的那样,insert的重载也需要一个范围:

template< class InputIt >
void insert( InputIt first, InputIt last );

因此,就像调用构造函数一样,您可以collection.insert(p, p + count);

不能保证这种重载会更有效,因为平均而言,这两种重载的复杂度都是线性的,而且仅一个元素一次插入即可。

实际上,如果我们研究如何在MSVC中实现insert,这非常简单

template<class _Iter>
void insert(_Iter _First, _Iter _Last)
{   // insert [_First, _Last) at front, then put in place
    _DEBUG_RANGE(_First, _Last);
    for (; _First != _Last; ++_First)
        emplace(*_First);
}

因此在这种情况下没有优化。

我认为,执行此操作的最佳方法是调用reserve,如果您知道要添加的元素数量很多,并且发生了很多冲突(整数不会这样) ),可能会修改bucket_count

答案 1 :(得分:2)

使用基于范围的构造函数或insert方法将简洁明了,但可能与您的方法一样有效。 原因是传递给这些函数的迭代器是输入迭代器,而不是随机迭代器。 因此,无法计算范围的长度,并且当集合的负载系数变高时,必须定期对这些元素进行逐次插入,以进行插入。

考虑调用std :: unordered_set的reserve方法。

collection.reserve(pi.size());
collection.insert(pi.begin(), pi.end());

编辑: 如评论中所述,人们还可能担心散列插入的元素的效率。 这样便能够执行某种形式的批量插入将是高效的。 但是,在OP的情况下,元素是整数,在大多数(如果不是全部)std :: hash实现中,它会使用标识函数进行哈希处理,而代价并不高;)。确实,它是随机整数可以获得的最佳哈希函数。其他散列函数可能更适合“有组织的”集合。

EDIT2: 现在,注释部分正在考虑哪种方法可以更好地实现insert方法。 我坚持认为,基于范围的插入重载会要求输入迭代器,因此,是的,您实际上可以传递任何非输出迭代器。 还可以看一下范围插入的最坏情况的复杂性:您会看到它是经过指定的,因此可以一一插入元素。 最后,看一下insert方法的一些实现,您会发现随机访问迭代器没有特别的重载。 这很有意义,因为在这里我们要将容器设置为至少给定容量的情况下,在保留方法在这里的情况下,没有理由对插入方法进行额外的检查。 基于此,以上答案很可能是基于stdlib实际实现的最佳技术。