unordered_set是以key为元素无序的关联容器,搜索、移除和插入操作是平均常数的时间复杂度。unordered_set在内部没有按任何顺序排列,而是放在桶当中的,放进哪个桶是通过计算key的hash值来决定的。
template<class Key,class Hash = std::hash<Key>,class KeyEqual = std::equal_to<Key>,class Allocator = std::allocator<Key>
> class unordered_set;
本文章的代码库:
https://gitee.com/gamestorm577/CppStd
成员函数
构造、析构和赋值
构造函数
可以构造一个空的unordered_set,也可以用迭代器、另一个unordered_set或者元素列表来构造一个unordered_set。构造的时候还可以指定最小桶数、hash函数、比较函数或者分配器。代码示例:
std::vector<int> tmp{1, 2, 3, 4};std::unordered_set<int> s1;
std::unordered_set<int> s2(tmp.begin(), tmp.end());
std::unordered_set<int> s3(s2);
std::unordered_set<int> s4{1, 2, 3};std::cout << "s1 size = " << s1.size() << std::endl;
std::cout << "s2 size = " << s2.size() << std::endl;
std::cout << "s3 size = " << s3.size() << std::endl;
std::cout << "s4 size = " << s4.size() << std::endl;
输出结果:
s1 size = 0
s2 size = 4
s3 size = 4
s4 size = 3
对于自定义的类型,需要定义hash函数以及比较函数。代码示例:
struct MyStruct
{int Num1;double Num2;
};struct MyHash
{std::size_t operator()(const MyStruct& val) const{return std::hash<int>()(val.Num1) + std::hash<double>()(val.Num2);}
};struct MyEqual
{bool operator()(const MyStruct& lhs, const MyStruct& rhs) const{return true;}
};std::unordered_set<MyStruct, MyHash, MyEqual> s;
析构函数
销毁unordered_set时,会调用各元素的析构函数。代码示例:
struct MyStruct
{MyStruct(int i): Num(i){}~MyStruct(){std::cout << "destruct, Num = " << Num << std::endl;}int Num = 0;
};struct MyHash
{std::size_t operator()(const MyStruct& val) const{return std::hash<int>()(val.Num);}
};struct MyEqual
{bool operator()(const MyStruct& lhs, const MyStruct& rhs) const{return lhs.Num == rhs.Num;}
};std::unordered_set<MyStruct, MyHash, MyEqual> s{1, 2, 3};
std::cout << "end" << std::endl;
输出结果:
destruct, Num = 3
destruct, Num = 2
destruct, Num = 1
end
destruct, Num = 3
destruct, Num = 2
destruct, Num = 1
赋值函数
可以用另一个unordered_set或者元素列表给unordered_set赋值。代码示例:
std::unordered_set<int> tmp{1, 2, 3};
std::unordered_set<int> s1;
std::unordered_set<int> s2;
s1 = tmp;
s2 = {1, 2, 3, 4};
std::cout << "s1 size = " << s1.size() << std::endl;
std::cout << "s2 size = " << s2.size() << std::endl;
输出结果:
s1 size = 3
s2 size = 4
迭代器
接口begin、cbegin指向unordered_set起始的迭代器,end、cend指向末尾的迭代器。无论什么迭代器都不能修改元素的值。代码示例:
std::unordered_set<int> s{1, 2, 3};
for (auto iter = s.begin(); iter != s.end(); ++iter)
{std::cout << "num = " << *iter << std::endl;
}
输出结果:
num = 3
num = 2
num = 1
容量
empty
检查unordered_set是否为空。代码示例:
std::unordered_set<int> s1{1, 2, 3};
std::unordered_set<int> s2;
std::cout << std::boolalpha;
std::cout << "s1 empty: " << s1.empty() << std::endl;
std::cout << "s2 empty: " << s2.empty() << std::endl;
输出结果:
s1 empty: false
s2 empty: true
size
获取unordered_set的元素个数。代码示例:
std::unordered_set<int> s1{1, 2, 3};
std::unordered_set<int> s2;
std::cout << "s1 size: " << s1.size() << std::endl;
std::cout << "s2 size: " << s2.size() << std::endl;
输出结果:
s1 size: 3
s2 size: 0
max_size
返回可以容纳的最大元素个数。代码示例:
std::unordered_set<char> s1;
std::unordered_set<std::string> s2;
std::cout << "s1 max size = " << s1.max_size() << std::endl;
std::cout << "s2 max size = " << s2.max_size() << std::endl;
输出结果:
s1 max size = 768614336404564650
s2 max size = 461168601842738790
修改器
clear
清除所有的元素。代码示例:
std::unordered_set<int> s{1, 2, 3, 4};
std::cout << "s size = " << s.size() << std::endl;
s.clear();
std::cout << "s size = " << s.size() << std::endl;
输出结果:
s size = 4
s size = 0
insert
插入元素,参数可以是元素、迭代器或者元素节点。代码示例:
std::unordered_set<int> m;m.insert(1);
m.insert(2);
std::cout << "m size = " << m.size() << std::endl;std::vector<int> tmp{3, 4};
m.insert(tmp.begin(), tmp.end());
std::cout << "m size = " << m.size() << std::endl;
输出结果:
m size = 2
m size = 4
emplace
构造一个元素到unordered_set中。代码示例:
struct MyStruct
{MyStruct(int num1, int num2): Num1(num1), Num2(num2){std::cout << "construct: " << num1 << " " << num2 << std::endl;}int Num1 = 0;int Num2 = 0;
};struct MyHash
{std::size_t operator()(const MyStruct& val) const{return std::hash<int>()(val.Num1) + std::hash<int>()(val.Num2);}
};struct MyEqual
{bool operator()(const MyStruct& lhs, const MyStruct& rhs) const{return (lhs.Num1 == rhs.Num1) && (lhs.Num2 == rhs.Num2);}
};std::unordered_set<MyStruct, MyHash, MyEqual> s;
s.emplace(1, 2);
s.emplace(3, 4);
输出结果:
construct: 1 2
construct: 3 4
emplace_hint
向unordered_set中尽可能靠近hint之前的位置插入新元素:
template <class... Args>
iterator emplace_hint(const_iterator hint, Args&&... args);
不同的hint会导致插入元素的效率不同。代码示例:
auto timer = [](std::function<std::size_t()> func, std::string tag) -> void
{auto start = std::chrono::system_clock::now();std::size_t size = func();auto end = std::chrono::system_clock::now();std::chrono::duration<double, std::milli> time = end - start;std::cout << tag << ", size: " << size << ", use time: " << time.count()<< std::endl;
};const int count = 3000000;auto unordered_set_emplace = [=]() -> std::size_t
{std::unordered_set<int> m;for (int i = 0; i < count; ++i){m.emplace(i);}return m.size();
};auto unordered_set_emplace_hint1 = [=]() -> std::size_t
{std::unordered_set<int> m;auto iter = m.begin();for (int i = 0; i < count; ++i){m.emplace_hint(iter, i);iter = m.end();}return m.size();
};auto unordered_set_emplace_hint2 = [=]() -> std::size_t
{std::unordered_set<int> m;auto iter = m.begin();for (int i = 0; i < count; ++i){m.emplace_hint(iter, i);iter = m.begin();}return m.size();
};auto unordered_set_emplace_hint3 = [=]() -> std::size_t
{std::unordered_set<int> m;auto iter = m.begin();for (int i = 0; i < count; ++i){iter = m.emplace_hint(iter, i);}return m.size();
};timer(unordered_set_emplace, "unordered_set_emplace");
timer(unordered_set_emplace_hint1, "unordered_set_emplace_hint1");
timer(unordered_set_emplace_hint2, "unordered_set_emplace_hint2");
timer(unordered_set_emplace_hint3, "unordered_set_emplace_hint3");
输出结果:
unordered_set_emplace, size: 3000000, use time: 490.632
unordered_set_emplace_hint1, size: 3000000, use time: 492.086
unordered_set_emplace_hint2, size: 3000000, use time: 508.571
unordered_set_emplace_hint3, size: 3000000, use time: 457.981
erase
移除指定位置的元素或者移除指定的值。代码示例:
std::unordered_set<int> s{1, 2, 3, 4, 5, 6, 7, 8, 9};
std::cout << "s size = " << s.size() << std::endl;
s.erase(s.begin());
std::cout << "s size = " << s.size() << std::endl;
s.erase(s.begin(), std::next(s.begin(), 3));
std::cout << "s size = " << s.size() << std::endl;
s.erase(*s.begin());
std::cout << "s size = " << s.size() << std::endl;
输出结果:
s size = 9
s size = 8
s size = 5
s size = 4
swap
和另一个unordered_set交换元素内容。代码示例:
std::unordered_set<int> s1{1, 2, 3, 4};
std::unordered_set<int> s2{1, 2};
s1.swap(s2);
std::cout << "s1 size = " << s1.size() << std::endl;
std::cout << "s2 size = " << s2.size() << std::endl;
输出结果:
s1 size = 2
s2 size = 4
extract
提取unordered_set中的某个元素节点,提取后unordered_set不再拥有该元素。代码示例:
std::unordered_set<int> s{1, 2, 3, 4, 5, 6, 7, 8, 9};
auto node1 = s.extract(1);
std::cout << "s size = " << s.size() << std::endl;
auto node2 = s.extract(s.begin());
std::cout << "s size = " << s.size() << ", node2 = " << node2.value() << std::endl;
s.insert(std::move(node2));
std::cout << "s size = " << s.size() << std::endl;
输出结果:
s size = 8
s size = 7, node2 = 9
s size = 8
merge
合并另一个unordered_set或者unordered_multiset中的元素。代码示例:
std::unordered_set<int> s1{1, 2, 3};
std::unordered_set<int> s2{3, 4};
s1.merge(s2);
std::cout << "s1 size = " << s1.size() << std::endl;
输出结果:
s1 size = 4
查找
count
获取给定key值的元素数量。由于unordered_set的元素是不重复的,结果只能是0或者1。代码示例:
std::unordered_set<int> s{1, 2, 3};
std::cout << "elment 1 count = " << s.count(1) << std::endl;
std::cout << "elment 3 count = " << s.count(4) << std::endl;
输出结果:
elment 1 count = 1
elment 3 count = 0
find
获取指定key值的元素的迭代器。代码示例:
std::unordered_set<int> s{1, 2, 3};
auto iter1 = s.find(1);
auto iter2 = s.find(4);
std::cout << std::boolalpha;
std::cout << "elment has 1: " << (iter1 == s.end()) << std::endl;
std::cout << "elment has 3: " << (iter2 == s.end()) << std::endl;
输出结果:
elment has 1: false
elment has 3: true
contains
检查是否包含特定的元素。代码示例:
std::unordered_set<int> s{1, 2, 3};
std::cout << std::boolalpha;
std::cout << "contain 1: " << s.contains(1) << std::endl;
std::cout << "contain 3: " << s.contains(4) << std::endl;
输出结果:
contain 1: true
contain 3: false
equal_range
获取容器中等于给定key值的元素范围。返回第一个迭代器指向范围的首元素,第二个迭代器指向范围的最后一个元素。代码示例:
std::unordered_set<int> s{1, 2, 3, 4, 5, 6, 7, 8};
for (auto item : s)
{std::cout << item << " ";
}
std::cout << std::endl;auto [iter1, iter2] = s.equal_range(3);
std::cout << "iter1 = " << *iter1 << std::endl;
std::cout << "iter2 = " << *iter2 << std::endl;
输出结果:
8 7 6 5 4 3 2 1
iter1 = 3
iter2 = 2
桶接口
bucket_count
返回unordered_set的桶数量。代码示例:
std::unordered_set<int> s{1, 2, 3, 4, 5, 6, 7, 8};
std::cout << "bucket count = " << s.bucket_count() << std::endl;
输出结果:
bucket count = 11
max_bucket_count
返回unordered_set可以容纳的最大桶数量。代码示例:
std::unordered_set<int> s;
std::cout << "max bucket count = " << s.max_bucket_count() << std::endl;
输出结果:
max bucket count = 768614336404564650
bucket_size
范围特定桶中的元素数量。代码示例:
std::unordered_set<int> s{1, 2, 3, 4, 5, 6, 7, 8};
for (int i = 0; i < s.bucket_count(); ++i)
{std::cout << "bucket " << i << " has item num " << s.bucket_size(i)<< std::endl;
}
输出结果:
bucket 0 has item num 0
bucket 1 has item num 1
bucket 2 has item num 1
bucket 3 has item num 1
bucket 4 has item num 1
bucket 5 has item num 1
bucket 6 has item num 1
bucket 7 has item num 1
bucket 8 has item num 1
bucket 9 has item num 0
bucket 10 has item num 0
bucket
返回给定key值的元素所在桶的索引。代码示例:
std::unordered_set<int> s{11, 12, 13, 14, 15, 16, 17, 18};
auto n = s.bucket(13);
std::cout << "item 13 is in bucket " << n << std::endl;
输出结果:
item 13 is in bucket 2
begin、cbegin、end、cend
begin和cbegin返回索引为n的桶中的首个元素的迭代器。end和cend返回索引为n的桶中的末尾迭代器。代码示例:
std::unordered_set<int> s{11, 12, 13, 14};
auto cnt = s.bucket(13);
auto iter_begin = s.begin(cnt);
auto iter_end = s.end(cnt);
for (auto iter = iter_begin; iter != iter_end; ++iter)
{std::cout << "num = " << *iter << std::endl;
}
输出结果:
num = 13
散列策略
load_factor
返回每个桶的平均元素数量。代码示例:
std::unordered_set<int> s{11, 12, 13, 14};
float num = s.load_factor();
std::cout << "load factor is: " << num << std::endl;
输出结果:
load factor is: 0.8
max_load_factor
没有参数的情况下返回最大平均桶数。参数为float类型时设置平均最大桶数,如果超出了该数量,容器就会自己增加桶数。代码示例:
int n_count = 200;std::unordered_set<int> s1;
s1.max_load_factor(1);
for (int i = 0; i < n_count; ++i)
{s1.insert(i);
}
std::cout << "s1 max load factor is: " << s1.max_load_factor() << std::endl;
std::cout << "s1 bucket count: " << s1.bucket_count() << std::endl;std::unordered_set<int> s2;
s2.max_load_factor(20);
for (int i = 0; i < n_count; ++i)
{s2.insert(i);
}
std::cout << "s2 max load factor is: " << s2.max_load_factor() << std::endl;
std::cout << "s2 bucket count: " << s2.bucket_count() << std::endl;
输出结果:
s1 max load factor is: 1
s1 bucket count: 397
s2 max load factor is: 20
s2 bucket count: 11
rehash
设置桶的最小数量并重新散列容器。代码示例:
std::unordered_set<int> s;
for (int i = 0; i < 200; ++i)
{s.insert(i);
}std::cout << "bucket cnt: " << s.bucket_count() << std::endl;
s.rehash(s.bucket_count() / 2);
std::cout << "bucket cnt: " << s.bucket_count() << std::endl;
s.rehash(s.bucket_count() * 4);
std::cout << "bucket cnt: " << s.bucket_count() << std::endl;
输出结果:
bucket cnt: 397
bucket cnt: 211
bucket cnt: 853
reserve
设置桶的最小元素个数,并重新散列容器。重新散列后的容器的平均桶数不能超过设定的值。代码示例:
std::unordered_set<int> s;
for (int i = 0; i < 200; ++i)
{s.insert(i);
}
std::cout << "bucket cnt: " << s.load_factor() << std::endl;s.rehash(5);
std::cout << "bucket cnt: " << s.load_factor() << std::endl;
观察器
hash_function
返回hash计算的函数。代码示例:
std::unordered_set<std::string> s;
auto hash_func = s.hash_function();
std::cout << "hash hello world is: " << hash_func("hello world") << std::endl;
输出结果:
hash hello world is: 12386028635079221413
key_eq
返回用于比较key相等性的函数。代码示例:
std::unordered_set<int> s;
auto key_eq_func = s.key_eq();
std::cout << std::boolalpha;
std::cout << key_eq_func(1, 1) << std::endl;
std::cout << key_eq_func(1, 2) << std::endl;
输出结果:
true
false
非成员函数
比较运算符
比较两个unordered_set是否相等。代码示例:
std::unordered_set<int> s1{1, 2, 3};
std::unordered_set<int> s2{1, 2};
std::cout << std::boolalpha;
std::cout << "s1 == s2: " << (s1 == s2) << std::endl;
std::cout << "s1 != s2: " << (s1 != s2) << std::endl;
输出结果:
s1 == s2: false
s1 != s2: true
swap
交换两个unordered_set。代码示例:
std::unordered_set<int> s1{1, 2, 3};
std::unordered_set<int> s2{1, 2};
std::swap(s1, s2);
std::cout << "s1 size = " << s1.size() << std::endl;
std::cout << "s2 size = " << s2.size() << std::endl;
输出结果:
s1 size = 2
s2 size = 3
erase_if
删除满足条件的元素。代码示例:
std::unordered_set<int> s = {1, 2, 3, 4};
std::cout << "s size = " << s.size() << std::endl;
std::erase_if(s,[](int a){return a > 2;});
std::cout << "s size = " << s.size() << std::endl;
输出结果:
s size = 4
s size = 2