我有一个连续存储在内存中的整数数组,我想将它们全部添加到unordered_set
集合中。
现在,我一次添加一个。
for (int i = 0; i < count; i++)
collection.insert(pi[i]);
有什么方法可以更有效地做到这一点?
我意识到这些项不是连续存储在集合中的,因此不会像将数组移交给集合那样简单。但这可以以某种方式进行优化吗?
答案 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实际实现的最佳技术。