跳到主要内容
注意

请注意,本文尚未完成!您可以通过编辑此文档页面来提供帮助。

概述

template< class Key, /* ... */ >
class unordered_set;

std::unordered_set 是一个用于存储唯一对象的关联容器。

使用示例

本节中的示例非常简单。有关更多示例,请导航到底部的示例部分

使用CTAD

#include <unordered_set>

int main() {
std::unordered_set ints{1, 2, 3, 2, 1, 4, 5, 5};

// Content: 1, 2, 3, 4, 5
}
元素顺序

重要的是要注意,尽管示例注释中选择了任意顺序,但内部元素保持无序(正如容器名称所示),这意味着我们在遍历无序集合时不能保证有任何特定顺序。

技术细节

unordered_set 的技术定义

std::unordered_set 是一个关联容器,包含一组类型为 Key 的唯一对象。

搜索、插入和移除具有平均常数时间复杂度。

在内部,元素不按任何特定顺序排序,而是组织成桶。元素放置在哪个桶中完全取决于其值的哈希。这允许快速访问单个元素,因为一旦计算出哈希,它就指向元素所放置的确切桶。

容器元素不能被修改(即使是非 const 迭代器),因为修改可能会改变元素的哈希并破坏容器。

std::unordered_set 符合 ContainerAllocatorAwareContainerUnorderedAssociativeContainer 的要求。

std::unordered_set

定义于unordered_set

模板参数

公有Key存储的键的类型。
公有哈希

一个对 Key 类型对象执行哈希处理的函数对象。

公有Compare

满足 Compare 的比较器类型。

公有Allocator

负责分配和释放内存的分配器类型。必须满足 Allocator

类型名称

公有key_typeKey
公有value_typeKey
公有size_type

无符号整数类型(通常为 std::size_t)。

公有difference_type

有符号整数类型(通常为 std::ptrdiff_t)。

公有hasher哈希
公有key_equalKeyEqual
公有allocator_typeAllocator
公有referencevalue_type&
公有const_referenceconst value_type&
公有pointerstd::allocator_traits<Allocator>::pointer
公有const_pointerstd::allocator_traits<Allocator>::const_pointer
公有iterator

指向 value_type 的常数 LegacyBidirectionalIterator

公有const_iterator

指向 const value_typeLegacyBidirectionalIterator

公有local_iterator

iterator,但仅用于遍历单个桶。

公有const_local_iterator

const_iterator,但仅用于遍历单个桶。

公有node_type (C++17 起)

表示容器节点的 节点句柄 的特化。

公有insert_return_type (C++17起)

描述插入 node_type 结果的类型,它是

template < class Iter, class NodeType >
struct /* unspecified */ {
Iter position;
bool inserted;
NodeType node;
};

使用模板参数 iteratornode_type 实例化的特化。

无序集合的迭代器

iterator 被称为 constant,因为它不可能修改其指向的值。这是因为修改迭代器指向的值可能会使无序集合的内部结构失效,从而导致各种不正确和意外的行为。

成员类型别名 iteratorconst_iterator 可能相同。这完全取决于实现。这意味着任何仅在迭代器类型上不同的重载函数/特化可能是不正确的,并可能导致 ODR 违规

迭代器失效

公有
操作
失效
公有所有只读操作,swap, std::swap从不
公有Clear, rehash, reverse, operator=总是
公有Insert, emplace, emplace_hint仅当导致重新哈希时
公有Erase仅对被删除的元素

成员函数

公有(构造函数)

构造一个新的无序集合。

公有(析构函数)

销毁一个无序集合。

公有operator=

将一个无序集合赋值给另一个。

公有get_allocator

返回关联的分配器。

迭代器

公有begin
cbegin

返回指向开头的迭代器。

公有end
cend

返回指向末尾的迭代器。

容量

公有empty

如果集合为空,则返回 true,否则返回 false

公有size

返回无序集合中的元素数量。

公有max_size

返回最大可能的元素数量。

修饰符

公有clear

清除无序集合的内容。

公有insert

插入元素(或节点(用 .extract() 提取)(自 C++17 起))。

公有emplace

就地构造新元素。

公有emplace_hint

使用提示(迭代器)就地构造元素。

公有erase

擦除元素。

公有swap

交换两个无序集合。

公有extract (C++17 起)

从无序集合中提取节点(可以稍后插入到其他地方)。

公有merge (C++17 起)

将两个无序集合合并在一起。

查找

公有count

返回与特定键匹配的元素数量。

公有find

搜索元素并返回指向它的迭代器,如果未找到则返回结束迭代器。

公有contains (C++20 起)

如果元素在集合中,则返回 true,否则返回 false

公有equal_range

返回与特定键匹配的元素范围。

桶接口

公有begin (size_type)
cbegin (size_type)

返回指向指定桶开头的迭代器。

公有end (size_type)
cend (size_type)

返回指向指定桶末尾的迭代器。

公有bucket_count

返回桶的数量。

公有max_bucket_count

返回最大桶数量。

公有bucket_size

返回特定桶中元素的数量。

公有bucket

返回特定键的桶。

哈希策略

公有load_factor

返回每个桶的平均元素数量。

公有max_load_factor

管理每个桶的最大平均元素数量。

公有rehash

至少保留指定数量的桶并重新生成哈希表。

公有reserve

为至少指定数量的元素保留空间并重新生成哈希表。

观察器

公有hash_function

返回哈希键的内部函数对象。

公有key_eq

返回一个比较键的内部函数对象。

非成员函数

公有operator==
operator!= (C++20 中移除)
比较 unordered_map 中的值。
公有std::swap (std::unordered_set)std::swap 算法的重载。
公有std::erase_if (std::unordered_set) (自 C++20 起)std::erase_if 算法的重载。

推导指南 (C++17 起)

点击展开

推导指南

// (1)
template< class InputIt,
class Hash = std::hash<iter_val_t<InputIt>>,
class Pred = std::equal_to<iter_val_t<InputIt>>,
class Alloc = std::allocator<iter_val_t<InputIt>> >
unordered_set( InputIt, InputIt,
typename /*see below*/::size_type = /*see below*/,
Hash = Hash(), Pred = Pred(), Alloc = Alloc() )
-> unordered_set<iter_val_t<InputIt>, Hash, Pred, Alloc>;
// (2)
template< class InputIt, class Alloc >
unordered_set( InputIt, InputIt, typename /*see below*/::size_type, Alloc )
-> unordered_set<iter_val_t<InputIt>,
std::hash<iter_val_t<InputIt>>,
std::equal_to<iter_val_t<InputIt>>,
Alloc>;
// (3)
template< class InputIt, class Hash, class Alloc >
unordered_set( InputIt, InputIt, typename /*see below*/::size_type, Hash, Alloc )
-> unordered_set<iter_val_t<InputIt>, Hash,
std::equal_to<iter_val_t<InputIt>>, Allocator>;

(1 - 3) 允许从迭代器范围推导

// (4)
template< class T, class Allocator >
unordered_set( std::initializer_list<T>, typename /*see below*/::size_type, Allocator )
-> unordered_set<T, std::hash<T>, std::equal_to<T>, Alloc>;
// (5)
template< class T, class Hash, class Alloc >
unordered_set( std::initializer_list<T>, typename /*see below*/::size_type, Hash, Alloc )
-> unordered_set<T, Hash, std::equal_to<T>, Alloc>;
// (6)
template< class T,
class Hash = std::hash<T>,
class Pred = std::equal_to<T>,
class Alloc = std::allocator<T> >
unordered_set( std::initializer_list<T>,
typename /*see below*/::size_type = /*see below*/,
Hash = Hash(), Pred = Pred(), Alloc = Alloc() )
-> unordered_set<T, Hash, Pred, Alloc>;

(4- 6) 允许从 std::initializer_list 推导

别名 iter_val_t 的定义如下

template< class InputIt >
using iter_val_t = typename std::iterator_traits<InputIt>::value_type;
注意

请注意,此别名不保证在标准库的任何地方找到。它仅用于这些推导指南的演示目的,并且在编写本文档时不存在于标准库中。

size_type 参数类型指的是推导指南推导出的类型的 size_type 成员类型。

重载决议

为了使任何推导指南参与重载决议,必须满足以下要求

注意

库确定类型不满足 LegacyInputIterator 的程度是未指定的,但至少

  • 整型不符合输入迭代器的条件。

同样,它确定类型不满足 Allocator 的程度是未指定的,但至少

更多示例

创建自定义哈希和比较器。

我们可以通过定义重载 operator() 的函数对象来创建自己的比较器。要为对象创建自定义哈希,我们可以在 std 命名空间中为其创建特化。

#include <iostream>
#include <string>
#include <unordered_set>

struct student {
std::string name;
std::string surname;
int age;

bool operator==(const student&) const noexcept = default;
};

/*
* We need to define a specialization for std::hash for the unordered_set to work
* properly.
*/
namespace std {

template <>
struct hash<student> {
std::size_t operator()(const student& obj) const noexcept {
constexpr auto str_hash = std::hash<std::string>();

return (str_hash(obj.name) * 31) ^ (str_hash(obj.surname) * 31) ^ obj.age;
}
};

}

struct student_comparator {
bool operator()(const student& first, const student& second) const noexcept {
return first == second;
}
};

auto main() -> int {
using student_set = std::unordered_set<student, std::hash<student>, student_comparator>;

const auto student1 = student("Philip", "Davies", 17);
const auto student2 = student("Philip", "Davies", 17);
const auto student3 = student("Philip", "Smith", 19);
const auto student4 = student("Jacob", "Jones", 19);

auto students = student_set{student1, student2, student3, student4};

for(const auto& student : students) {
std::cout << "Student: " << student.name << ' '
<< student.surname << ", "
<< student.age << " years old.\n";
}
}
结果
Student: Jacob Jones, 19 years old.
Student: Philip Smith, 19 years old.
Student: Philip Davies, 17 years old.
自定义哈希

请注意,尽管我们已将四名学生添加到集合中,但我们只看到其中三名学生的信息被显示。这是因为有两名学生具有完全相同的数据。当比较它们两者的哈希时,它们被发现是相同的,这导致比较这两个对象本身(使用 operator==,因此它存在于 student 中)。如果比较测试为真,则不添加新对象(与已插入的对象比较相同)。

本文档源自 此 CppReference 页面。它可能为了改进或编辑者的偏好而进行了修改。点击“编辑此页面”查看本文档的所有更改。
悬停查看原始许可证。
注意

请注意,本文尚未完成!您可以通过编辑此文档页面来提供帮助。

概述

template< class Key, /* ... */ >
class unordered_set;

std::unordered_set 是一个用于存储唯一对象的关联容器。

使用示例

本节中的示例非常简单。有关更多示例,请导航到底部的示例部分

使用CTAD

#include <unordered_set>

int main() {
std::unordered_set ints{1, 2, 3, 2, 1, 4, 5, 5};

// Content: 1, 2, 3, 4, 5
}
元素顺序

重要的是要注意,尽管示例注释中选择了任意顺序,但内部元素保持无序(正如容器名称所示),这意味着我们在遍历无序集合时不能保证有任何特定顺序。

技术细节

unordered_set 的技术定义

std::unordered_set 是一个关联容器,包含一组类型为 Key 的唯一对象。

搜索、插入和移除具有平均常数时间复杂度。

在内部,元素不按任何特定顺序排序,而是组织成桶。元素放置在哪个桶中完全取决于其值的哈希。这允许快速访问单个元素,因为一旦计算出哈希,它就指向元素所放置的确切桶。

容器元素不能被修改(即使是非 const 迭代器),因为修改可能会改变元素的哈希并破坏容器。

std::unordered_set 符合 ContainerAllocatorAwareContainerUnorderedAssociativeContainer 的要求。

std::unordered_set

定义于unordered_set

模板参数

公有Key存储的键的类型。
公有哈希

一个对 Key 类型对象执行哈希处理的函数对象。

公有Compare

满足 Compare 的比较器类型。

公有Allocator

负责分配和释放内存的分配器类型。必须满足 Allocator

类型名称

公有key_typeKey
公有value_typeKey
公有size_type

无符号整数类型(通常为 std::size_t)。

公有difference_type

有符号整数类型(通常为 std::ptrdiff_t)。

公有hasher哈希
公有key_equalKeyEqual
公有allocator_typeAllocator
公有referencevalue_type&
公有const_referenceconst value_type&
公有pointerstd::allocator_traits<Allocator>::pointer
公有const_pointerstd::allocator_traits<Allocator>::const_pointer
公有iterator

指向 value_type 的常数 LegacyBidirectionalIterator

公有const_iterator

指向 const value_typeLegacyBidirectionalIterator

公有local_iterator

iterator,但仅用于遍历单个桶。

公有const_local_iterator

const_iterator,但仅用于遍历单个桶。

公有node_type (C++17 起)

表示容器节点的 节点句柄 的特化。

公有insert_return_type (C++17起)

描述插入 node_type 结果的类型,它是

template < class Iter, class NodeType >
struct /* unspecified */ {
Iter position;
bool inserted;
NodeType node;
};

使用模板参数 iteratornode_type 实例化的特化。

无序集合的迭代器

iterator 被称为 constant,因为它不可能修改其指向的值。这是因为修改迭代器指向的值可能会使无序集合的内部结构失效,从而导致各种不正确和意外的行为。

成员类型别名 iteratorconst_iterator 可能相同。这完全取决于实现。这意味着任何仅在迭代器类型上不同的重载函数/特化可能是不正确的,并可能导致 ODR 违规

迭代器失效

公有
操作
失效
公有所有只读操作,swap, std::swap从不
公有Clear, rehash, reverse, operator=总是
公有Insert, emplace, emplace_hint仅当导致重新哈希时
公有Erase仅对被删除的元素

成员函数

公有(构造函数)

构造一个新的无序集合。

公有(析构函数)

销毁一个无序集合。

公有operator=

将一个无序集合赋值给另一个。

公有get_allocator

返回关联的分配器。

迭代器

公有begin
cbegin

返回指向开头的迭代器。

公有end
cend

返回指向末尾的迭代器。

容量

公有empty

如果集合为空,则返回 true,否则返回 false

公有size

返回无序集合中的元素数量。

公有max_size

返回最大可能的元素数量。

修饰符

公有clear

清除无序集合的内容。

公有insert

插入元素(或节点(用 .extract() 提取)(自 C++17 起))。

公有emplace

就地构造新元素。

公有emplace_hint

使用提示(迭代器)就地构造元素。

公有erase

擦除元素。

公有swap

交换两个无序集合。

公有extract (C++17 起)

从无序集合中提取节点(可以稍后插入到其他地方)。

公有merge (C++17 起)

将两个无序集合合并在一起。

查找

公有count

返回与特定键匹配的元素数量。

公有find

搜索元素并返回指向它的迭代器,如果未找到则返回结束迭代器。

公有contains (C++20 起)

如果元素在集合中,则返回 true,否则返回 false

公有equal_range

返回与特定键匹配的元素范围。

桶接口

公有begin (size_type)
cbegin (size_type)

返回指向指定桶开头的迭代器。

公有end (size_type)
cend (size_type)

返回指向指定桶末尾的迭代器。

公有bucket_count

返回桶的数量。

公有max_bucket_count

返回最大桶数量。

公有bucket_size

返回特定桶中元素的数量。

公有bucket

返回特定键的桶。

哈希策略

公有load_factor

返回每个桶的平均元素数量。

公有max_load_factor

管理每个桶的最大平均元素数量。

公有rehash

至少保留指定数量的桶并重新生成哈希表。

公有reserve

为至少指定数量的元素保留空间并重新生成哈希表。

观察器

公有hash_function

返回哈希键的内部函数对象。

公有key_eq

返回一个比较键的内部函数对象。

非成员函数

公有operator==
operator!= (C++20 中移除)
比较 unordered_map 中的值。
公有std::swap (std::unordered_set)std::swap 算法的重载。
公有std::erase_if (std::unordered_set) (自 C++20 起)std::erase_if 算法的重载。

推导指南 (C++17 起)

点击展开

推导指南

// (1)
template< class InputIt,
class Hash = std::hash<iter_val_t<InputIt>>,
class Pred = std::equal_to<iter_val_t<InputIt>>,
class Alloc = std::allocator<iter_val_t<InputIt>> >
unordered_set( InputIt, InputIt,
typename /*see below*/::size_type = /*see below*/,
Hash = Hash(), Pred = Pred(), Alloc = Alloc() )
-> unordered_set<iter_val_t<InputIt>, Hash, Pred, Alloc>;
// (2)
template< class InputIt, class Alloc >
unordered_set( InputIt, InputIt, typename /*see below*/::size_type, Alloc )
-> unordered_set<iter_val_t<InputIt>,
std::hash<iter_val_t<InputIt>>,
std::equal_to<iter_val_t<InputIt>>,
Alloc>;
// (3)
template< class InputIt, class Hash, class Alloc >
unordered_set( InputIt, InputIt, typename /*see below*/::size_type, Hash, Alloc )
-> unordered_set<iter_val_t<InputIt>, Hash,
std::equal_to<iter_val_t<InputIt>>, Allocator>;

(1 - 3) 允许从迭代器范围推导

// (4)
template< class T, class Allocator >
unordered_set( std::initializer_list<T>, typename /*see below*/::size_type, Allocator )
-> unordered_set<T, std::hash<T>, std::equal_to<T>, Alloc>;
// (5)
template< class T, class Hash, class Alloc >
unordered_set( std::initializer_list<T>, typename /*see below*/::size_type, Hash, Alloc )
-> unordered_set<T, Hash, std::equal_to<T>, Alloc>;
// (6)
template< class T,
class Hash = std::hash<T>,
class Pred = std::equal_to<T>,
class Alloc = std::allocator<T> >
unordered_set( std::initializer_list<T>,
typename /*see below*/::size_type = /*see below*/,
Hash = Hash(), Pred = Pred(), Alloc = Alloc() )
-> unordered_set<T, Hash, Pred, Alloc>;

(4- 6) 允许从 std::initializer_list 推导

别名 iter_val_t 的定义如下

template< class InputIt >
using iter_val_t = typename std::iterator_traits<InputIt>::value_type;
注意

请注意,此别名不保证在标准库的任何地方找到。它仅用于这些推导指南的演示目的,并且在编写本文档时不存在于标准库中。

size_type 参数类型指的是推导指南推导出的类型的 size_type 成员类型。

重载决议

为了使任何推导指南参与重载决议,必须满足以下要求

注意

库确定类型不满足 LegacyInputIterator 的程度是未指定的,但至少

  • 整型不符合输入迭代器的条件。

同样,它确定类型不满足 Allocator 的程度是未指定的,但至少

更多示例

创建自定义哈希和比较器。

我们可以通过定义重载 operator() 的函数对象来创建自己的比较器。要为对象创建自定义哈希,我们可以在 std 命名空间中为其创建特化。

#include <iostream>
#include <string>
#include <unordered_set>

struct student {
std::string name;
std::string surname;
int age;

bool operator==(const student&) const noexcept = default;
};

/*
* We need to define a specialization for std::hash for the unordered_set to work
* properly.
*/
namespace std {

template <>
struct hash<student> {
std::size_t operator()(const student& obj) const noexcept {
constexpr auto str_hash = std::hash<std::string>();

return (str_hash(obj.name) * 31) ^ (str_hash(obj.surname) * 31) ^ obj.age;
}
};

}

struct student_comparator {
bool operator()(const student& first, const student& second) const noexcept {
return first == second;
}
};

auto main() -> int {
using student_set = std::unordered_set<student, std::hash<student>, student_comparator>;

const auto student1 = student("Philip", "Davies", 17);
const auto student2 = student("Philip", "Davies", 17);
const auto student3 = student("Philip", "Smith", 19);
const auto student4 = student("Jacob", "Jones", 19);

auto students = student_set{student1, student2, student3, student4};

for(const auto& student : students) {
std::cout << "Student: " << student.name << ' '
<< student.surname << ", "
<< student.age << " years old.\n";
}
}
结果
Student: Jacob Jones, 19 years old.
Student: Philip Smith, 19 years old.
Student: Philip Davies, 17 years old.
自定义哈希

请注意,尽管我们已将四名学生添加到集合中,但我们只看到其中三名学生的信息被显示。这是因为有两名学生具有完全相同的数据。当比较它们两者的哈希时,它们被发现是相同的,这导致比较这两个对象本身(使用 operator==,因此它存在于 student 中)。如果比较测试为真,则不添加新对象(与已插入的对象比较相同)。

本文档源自 此 CppReference 页面。它可能为了改进或编辑者的偏好而进行了修改。点击“编辑此页面”查看本文档的所有更改。
悬停查看原始许可证。