一篇文章让你熟悉unordered_map及其模拟实现(中)

简介: unordered_map元素访问和元素查找函数1. operator[]mapped_type& operator[] ( const key_type& k );:这个重载函数接受一个const引用类型的键(key_type),并返回与该键关

unordered_map元素访问和元素查找函数

1. operator[]

  1. mapped_type& operator[] ( const key_type& k );
  • 这个重载函数接受一个const引用类型的键(key_type),并返回与该键关联的值(mapped_type)的引用。
  • 如果键(k)存在于unordered_map中,它将返回该键的值的引用,允许你修改该值。
  • 如果键(k)不存在,它将在unordered_map中插入该键-值对,并返回一个默认构造的值的引用(通常为该类型的默认值)。
  • 这意味着你可以通过operator[]来读取或修改unordered_map中的值,而不需要显式地检查键是否存在或插入键-值对。
  1. mapped_type& operator[] ( key_type&& k );
  • 这个重载函数接受一个右值引用类型的键(key_type),并返回与该键关联的值(mapped_type)的引用。
  • 与第一个重载函数类似,如果键(k)存在于unordered_map中,它将返回该键的值的引用,允许你修改该值。
  • 如果键(k)不存在,它将在unordered_map中插入该键-值对,并返回一个默认构造的值的引用。

以下是使用std::unordered_mapoperator[]重载函数的示例:

#include <iostream>
#include <unordered_map>
int main() {
    std::unordered_map<std::string, int> myMap;
    // 使用 operator[] 插入键-值对
    myMap["apple"] = 3;
    myMap["banana"] = 2;
    myMap["cherry"] = 5;
    // 访问键的值并修改它
    std::cout << "Number of apples: " << myMap["apple"] << std::endl;
    myMap["apple"] = 4;
    // 访问不存在的键,会插入默认值并返回引用
    std::cout << "Number of oranges: " << myMap["orange"] << std::endl;
    // 使用右值引用的 operator[] 插入键-值对
    std::string fruit = "grape";
    myMap[std::move(fruit)] = 6;
    // 遍历输出所有键-值对
    for (const auto& kv : myMap) {
        std::cout << kv.first << ": " << kv.second << std::endl;
    }
    return 0;
}

在这个示例中,我们使用operator[]插入键-值对,访问并修改已存在的键的值,以及访问不存在的键,它会自动插入默认值。还演示了使用右值引用的operator[]来插入新的键-值对。最后,我们遍历输出了所有键-值对。

2. at

  1. 修改值的 at 函数
mapped_type& at(const key_type& k);
mapped_type& at(key_type&& k);
  1. 这两个重载版本允许你根据键 k 修改关联容器中的值。如果键 k 存在于容器中,它会返回对应的值的引用,允许你修改这个值。如果键 k 不存在,它会抛出 std::out_of_range 异常。
    示例代码:
std::unordered_map<std::string, int> myMap;
myMap["apple"] = 3;
// 使用 at 函数修改值
myMap.at("apple") = 4;
  1. 如果键 "apple" 存在,它将修改值为 4
  2. 只读访问值的 at 函数
const mapped_type& at(const key_type& k) const;
  1. 这个重载版本允许你只读访问容器中的值。它返回与键 k 相关联的值的常量引用。如果键 k 不存在,它也会抛出 std::out_of_range 异常。
    示例代码:
std::unordered_map<std::string, int> myMap;
myMap["apple"] = 3;
// 使用 at 函数只读访问值
int numberOfApples = myMap.at("apple");
  1. 如果键 "apple" 存在,它将返回值 3,并且你可以将其存储在变量 numberOfApples 中。

总之,at 函数是一个用于安全地访问关联容器中元素的方法,因为它会检查键是否存在并在必要时抛出异常。这有助于避免访问不存在的键而导致的未定义行为。

3. find

  1. 非常量容器的 find 函数
iterator find(const key_type& k);
  1. 这个版本的 find 函数用于非常量容器,返回一个迭代器(iterator)。如果容器中存在键 k,则迭代器指向与键 k 相关联的元素;如果键 k 不存在,它返回容器的 end() 迭代器,表示未找到。
    示例代码:
std::unordered_map<std::string, int> myMap;
myMap["apple"] = 3;
// 使用 find 函数查找键 "apple"
auto it = myMap.find("apple");
if (it != myMap.end()) {
    // 找到了,输出值
    std::cout << "Value of 'apple': " << it->second << std::endl;
} else {
    // 未找到
    std::cout << "Key 'apple' not found." << std::endl;
}
  1. 常量容器的 find 函数
const_iterator find(const key_type& k) const;
  1. 这个版本的 find 函数用于常量容器,返回一个常量迭代器(const_iterator)。它的功能与非常量版本相同,但不允许修改容器中的元素。
    示例代码:
const std::unordered_map<std::string, int> myMap = {
    {"apple", 3},
    {"banana", 2},
    {"cherry", 5}
};
// 使用 const find 函数查找键 "banana"
auto it = myMap.find("banana");
if (it != myMap.end()) {
    // 找到了,输出值
    std::cout << "Value of 'banana': " << it->second << std::endl;
} else {
    // 未找到
    std::cout << "Key 'banana' not found." << std::endl;
}

总之,find 函数是一个用于在关联容器中查找特定键的非常有用的方法。如果你需要查找键是否存在并访问与之关联的值,find 函数是一个安全且高效的选择。

4. count

size_type count(const key_type& k) const;
  • k:要查找的键。
  • size_type:返回值类型,通常是一个无符号整数类型,表示键 k 在容器中的出现次数。

count 函数会在容器中查找键 k,并返回键的出现次数。如果键 k 存在于容器中,count 函数将返回 1 或更大的正整数,表示键出现的次数。如果键 k 不存在,函数将返回 0,表示键没有出现。

示例代码:

std::unordered_map<std::string, int> myMap = {
    {"apple", 3},
    {"banana", 2},
    {"cherry", 5}
};
// 计算键 "apple" 在容器中的出现次数
size_t appleCount = myMap.count("apple");
std::cout << "The count of 'apple': " << appleCount << std::endl;
// 计算键 "grape" 在容器中的出现次数
size_t grapeCount = myMap.count("grape");
std::cout << "The count of 'grape': " << grapeCount << std::endl;

在上面的示例中,count 函数首先计算键 “apple” 在容器中的出现次数(应为 1),然后计算键 “grape” 在容器中的出现次数(应为 0)。count 函数对于检查某个键是否存在于容器中以及统计键的出现次数非常有用。如果你需要知道某个键是否存在以及它出现了多少次,可以使用 count 函数进行查询。

5. equal_range

pair<iterator,iterator>
   equal_range ( const key_type& k );
pair<const_iterator,const_iterator>
   equal_range ( const key_type& k ) const;
  • k:要查找的键。
  • pair:返回值类型,一个包含两个迭代器的容器,表示范围的起始和结束位置。
  • iteratorconst_iterator:迭代器类型,表示容器的迭代器和常量迭代器。

equal_range 函数会在容器中查找键 k,然后返回一个 pair,其中第一个元素是指向范围的开始位置的迭代器,第二个元素是指向范围的结束位置的迭代器。这个范围包括了所有键等于 k 的元素。

示例代码:

std::map<int, std::string> myMap = {
    {1, "one"},
    {2, "two"},
    {2, "another two"}, // 注意:键 2 重复
    {3, "three"}
};
// 查找键 2 在容器中的范围
auto range = myMap.equal_range(2);
// 输出范围中的元素
for (auto it = range.first; it != range.second; ++it) {
    std::cout << it->first << ": " << it->second << std::endl;
}

在上面的示例中,equal_range 函数查找键 2 在 myMap 中的范围,并返回一个包含范围的起始和结束迭代器的 pair。然后,我们使用迭代器遍历范围,输出范围中的元素。

equal_range 函数在处理关联容器中的重复键时非常有用,因为它允许你查找所有具有相同键的元素,并迭代遍历它们。

unordered_map修饰符函数

1. emplace

template <class... Args>
pair<iterator, bool> emplace ( Args&&... args );

std::unordered_mapemplace 函数用于在哈希表中插入新的键值对,并返回一个 std::pair,其中包含一个迭代器和一个布尔值。

  • Args&&... args 是模板参数包,允许你传递任意数量的参数。
  • pair<iterator, bool> 是返回值类型,其中 iterator 是插入或已存在键值对的迭代器,bool 表示插入是否成功。如果键已存在,则迭代器指向已存在的键值对,布尔值为 false;如果键不存在,则迭代器指向新插入的键值对,布尔值为 true

以下是 emplace 函数的示例用法:

#include <iostream>
#include <unordered_map>
#include <string>
int main() {
    std::unordered_map<int, std::string> myMap;
    // 使用 emplace 插入键值对
    auto result1 = myMap.emplace(1, "One");
    if (result1.second) {
        std::cout << "Insertion successful. Key: " << result1.first->first << ", Value: " << result1.first->second << std::endl;
    } else {
        std::cout << "Key already exists. Key: " << result1.first->first << ", Value: " << result1.first->second << std::endl;
    }
    // 尝试再次插入相同的键值对
    auto result2 = myMap.emplace(1, "Another One");
    if (result2.second) {
        std::cout << "Insertion successful. Key: " << result2.first->first << ", Value: " << result2.first->second << std::endl;
    } else {
        std::cout << "Key already exists. Key: " << result2.first->first << ", Value: " << result2.first->second << std::endl;
    }
    return 0;
}

在上述示例中,首先使用 emplace 函数插入一个键值对,然后再次尝试插入相同的键值对。根据返回的布尔值可以确定插入是否成功,以及插入的键值对是已存在的还是新插入的。

2. emplace_hint

emplace 不同,emplace_hint 允许你提供一个提示位置,以提高插入操作的性能。

以下是对 emplace_hint 函数的参数和用法的简要说明:

  • position:这是一个迭代器,它指示了插入位置的提示。新元素将插入到 position 之前。这个参数可以帮助容器更高效地插入元素,但不是必需的。
  • Args&&... args:这是一个可变参数模板,用于传递新元素的构造参数。根据元素类型的构造函数,你可以传递任意数量的参数。
  • 返回值:emplace_hint 返回一个迭代器,指向插入的新元素,或者如果插入失败,则返回一个指向容器中现有元素的迭代器。

下面是一个示例,演示了如何使用 emplace_hintstd::unordered_map 中插入新的键值对:

#include <iostream>
#include <unordered_map>
int main() {
    std::unordered_map<int, std::string> myMap;
    // 使用 emplace_hint 插入元素
    auto hint = myMap.emplace_hint(myMap.begin(), 1, "One");
    myMap.emplace_hint(hint, 2, "Two");
    myMap.emplace_hint(hint, 3, "Three");
    // 遍历 unordered_map 并打印键值对
    for (const auto& pair : myMap) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }
    return 0;
}

在这个示例中,emplace_hint 被用来插入新的键值对,通过 hint 提示位置,以提高插入的效率。注意,emplace_hint 可以在任何位置插入元素,但通常你会使用提示位置以避免不必要的搜索和移动操作。

3. insert

  1. pair<iterator, bool> insert(const value_type& val):将一个键值对 val 插入到 unordered_map 中。如果插入成功,返回一个 pair,其中的 iterator 指向插入的元素,bool 表示插入是否成功。
    示例:
std::unordered_map<int, std::string> myMap;
std::pair<int, std::string> pairToInsert(1, "One");
auto result = myMap.insert(pairToInsert);
if (result.second) {
    std::cout << "Insertion successful." << std::endl;
} else {
    std::cout << "Insertion failed (key already exists)." << std::endl;
}
  1. template <class P> pair<iterator, bool> insert(P&& val):使用移动语义,将一个键值对 val 插入到 unordered_map 中。同样返回一个 pair,表示插入结果。
    示例:
std::unordered_map<int, std::string> myMap;
auto result = myMap.insert(std::make_pair(1, "One"));
if (result.second) {
    std::cout << "Insertion successful." << std::endl;
} else {
    std::cout << "Insertion failed (key already exists)." << std::endl;
}
  1. iterator insert(const_iterator hint, const value_type& val):在给定的位置 hint 处插入一个键值对 val。这个函数允许你提供一个位置提示,以提高插入效率。
    示例:
std::unordered_map<int, std::string> myMap;
auto hint = myMap.begin(); // 可以是任何迭代器位置
myMap.insert(hint, std::make_pair(1, "One"));
  1. template <class P> iterator insert(const_iterator hint, P&& val):与第三个函数类似,使用移动语义插入键值对,同时提供位置提示。
    示例:
std::unordered_map<int, std::string> myMap;
auto hint = myMap.begin(); // 可以是任何迭代器位置
myMap.insert(hint, std::make_pair(1, "One"));
  1. template <class InputIterator> void insert(InputIterator first, InputIterator last):用一对迭代器 [first, last) 指定的范围内的元素插入到 unordered_map 中。
    示例:
std::unordered_map<int, std::string> myMap;
std::vector<std::pair<int, std::string>> data = {{1, "One"}, {2, "Two"}, {3, "Three"}};
myMap.insert(data.begin(), data.end());
  1. void insert(initializer_list<value_type> il):使用初始化列表中的元素插入到 unordered_map 中。
    示例:
std::unordered_map<int, std::string> myMap;
myMap.insert({{1, "One"}, {2, "Two"}, {3, "Three"}});

这些函数提供了多种插入键值对的方式,使你可以根据需求选择最合适的方法。

4. erase

  1. iterator erase(const_iterator position):通过迭代器位置 position 删除对应键值对。返回指向删除元素之后位置的迭代器。
    示例:
std::unordered_map<int, std::string> myMap = {{1, "One"}, {2, "Two"}, {3, "Three"}};
auto it = myMap.find(2); // 找到键为2的位置
if (it != myMap.end()) {
    myMap.erase(it); // 删除键为2的元素
}
  1. size_type erase(const key_type& k):通过键 k 删除对应的键值对。返回删除的元素个数(0或1,因为键在 unordered_map 中是唯一的)。
    示例:
std::unordered_map<int, std::string> myMap = {{1, "One"}, {2, "Two"}, {3, "Three"}};
size_t erased = myMap.erase(2); // 删除键为2的元素
std::cout << "Erased " << erased << " element(s)." << std::endl;
  1. iterator erase(const_iterator first, const_iterator last):通过一对迭代器 [first, last) 删除指定范围内的键值对。返回指向删除操作之后位置的迭代器。
    示例:
std::unordered_map<int, std::string> myMap = {{1, "One"}, {2, "Two"}, {3, "Three"}};
auto first = myMap.begin(); // 范围起始位置
auto last = myMap.find(2);  // 范围结束位置,找到键为2的位置
if (last != myMap.end()) {
    myMap.erase(first, last); // 删除范围内的元素
}

这些函数提供了不同的方式来删除 unordered_map 中的元素,根据需求可以选择最合适的方法。

5. clear

  • void clear() noexcept;:清空 unordered_map 中的所有键值对。这个操作会使 unordered_map 变为空,但不会改变容器的容量。
    示例:
std::unordered_map<int, std::string> myMap = {{1, "One"}, {2, "Two"}, {3, "Three"}};
std::cout << "Size before clear: " << myMap.size() << std::endl;
myMap.clear(); // 清空 unordered_map
std::cout << "Size after clear: " << myMap.size() << std::endl;
  • 输出:
Size before clear: 3
Size after clear: 0

这个函数是一个无异常操作 (noexcept),意味着它不会抛出异常。它可以在需要清空 unordered_map 的情况下使用,以释放容器占用的资源。

6. swap

  • void swap ( unordered_map& ump );:交换当前 unordered_map 与参数 ump 所表示的 unordered_map 的内容。这个操作不会改变容器的容量,只是交换它们的内容。
    示例:
std::unordered_map<int, std::string> map1 = {{1, "One"}, {2, "Two"}};
std::unordered_map<int, std::string> map2 = {{3, "Three"}, {4, "Four"}};
std::cout << "map1 before swap:" << std::endl;
for (const auto& pair : map1) {
    std::cout << pair.first << ": " << pair.second << std::endl;
}
std::cout << "map2 before swap:" << std::endl;
for (const auto& pair : map2) {
    std::cout << pair.first << ": " << pair.second << std::endl;
}
map1.swap(map2); // 交换 map1 和 map2 的内容
std::cout << "map1 after swap:" << std::endl;
for (const auto& pair : map1) {
    std::cout << pair.first << ": " << pair.second << std::endl;
}
std::cout << "map2 after swap:" << std::endl;
for (const auto& pair : map2) {
    std::cout << pair.first << ": " << pair.second << std::endl;
}
  • 输出:
map1 before swap:
1: One
2: Two
map2 before swap:
3: Three
4: Four
map1 after swap:
3: Three
4: Four
map2 after swap:
1: One
2: Two

这个函数非常有用,因为它可以在不需要创建临时对象的情况下,高效地交换两个 unordered_map 的内容。

unordered_map桶函数

1. bucket_count

bucket_count 函数用于获取 std::unordered_map 容器中当前桶(buckets)的数量。桶是哈希表中的存储位置,用于存储键值对。

size_type bucket_count() const noexcept;
  • size_type:表示无符号整数类型,通常是 size_t
  • bucket_count():该函数没有参数。
  • const:表示该函数不会修改容器的内容。
  • noexcept:表示该函数不会抛出异常。

该函数返回当前 unordered_map 容器中桶的数量。

示例代码:

#include <iostream>
#include <unordered_map>
int main() {
    std::unordered_map<int, std::string> map;
    map[1] = "One";
    map[2] = "Two";
    map[3] = "Three";
    map[4] = "Four";
    std::cout << "Bucket count: " << map.bucket_count() << std::endl;
    return 0;
}

在这个示例中,bucket_count 函数返回当前 std::unordered_map 中的桶数量,该数量取决于哈希函数和负载因子等因素。这个数量通常会比容器中实际的元素数量大,以确保元素能够均匀分布在桶中,从而提高查询性能。

2. max_bucket_count

max_bucket_count 函数用于获取 std::unordered_map 容器支持的最大桶(buckets)数量。桶是哈希表中的存储位置,用于存储键值对。

size_type max_bucket_count() const noexcept;
  • size_type:表示无符号整数类型,通常是 size_t
  • max_bucket_count():该函数没有参数。
  • const:表示该函数不会修改容器的内容。
  • noexcept:表示该函数不会抛出异常。

该函数返回 std::unordered_map 容器支持的最大桶数量。这个值通常受到底层哈希表实现和系统资源限制的影响。

示例代码:

#include <iostream>
#include <unordered_map>
int main() {
    std::unordered_map<int, std::string> map;
    // 获取最大桶数量并输出
    std::cout << "Max bucket count: " << map.max_bucket_count() << std::endl;
    return 0;
}

在这个示例中,max_bucket_count 函数返回 std::unordered_map 容器支持的最大桶数量。这个值是由编译器和系统决定的,通常取决于系统的可用内存和哈希表实现。

3. bucket_size

bucket_size 函数用于获取指定桶中的元素数量,它需要传入一个桶的索引作为参数。

size_type bucket_size(size_type n) const;
  • size_type:表示无符号整数类型,通常是 size_t
  • bucket_size(n):该函数接受一个参数 n,表示要查询的桶的索引。
  • const:表示该函数不会修改容器的内容。

这个函数返回指定桶中的元素数量。

示例代码:

#include <iostream>
#include <unordered_map>
int main() {
    std::unordered_map<int, std::string> map;
    // 插入一些键值对
    map[1] = "one";
    map[2] = "two";
    map[3] = "three";
    // 获取第一个桶中的元素数量
    size_t bucketIndex = map.bucket(1); // 获取键为1的元素所在的桶的索引
    size_t bucketSize = map.bucket_size(bucketIndex); // 获取该桶的元素数量
    std::cout << "Bucket size for key 1: " << bucketSize << std::endl;
    return 0;
}

在这个示例中,我们创建了一个 std::unordered_map 容器,插入了一些键值对,并使用 bucket_size 函数获取特定桶中的元素数量。请注意,我们首先使用 bucket 函数获取了键为1的元素所在的桶的索引。然后,我们使用 bucket_size 函数获取了该桶中的元素数量。

4. bucket

bucket 函数用于获取给定键所属的桶的索引,它需要传入一个键作为参数。

size_type bucket(const key_type& k) const;
  • size_type:表示无符号整数类型,通常是 size_t
  • bucket(k):该函数接受一个参数 k,表示要查询的键。
  • const:表示该函数不会修改容器的内容。

这个函数返回指定键所属的桶的索引。

示例代码:

#include <iostream>
#include <unordered_map>
int main() {
    std::unordered_map<std::string, int> map;
    // 插入一些键值对
    map["one"] = 1;
    map["two"] = 2;
    map["three"] = 3;
    // 获取键所属的桶的索引
    size_t bucketIndex1 = map.bucket("one");
    size_t bucketIndex2 = map.bucket("three");
    std::cout << "Bucket index for key 'one': " << bucketIndex1 << std::endl;
    std::cout << "Bucket index for key 'three': " << bucketIndex2 << std::endl;
    return 0;
}

在这个示例中,我们创建了一个 std::unordered_map 容器,插入了一些键值对,并使用 bucket 函数获取特定键所属的桶的索引。我们分别获取了键 “one” 和 “three” 所在的桶的索引。

相关文章
|
6月前
|
C++ 容器
【C++】红黑树模拟实现STL中的map与set
【C++】红黑树模拟实现STL中的map与set
|
6月前
|
编译器 C++
【STL】:list的模拟实现
【STL】:list的模拟实现
52 0
|
C++ 容器
unordered_map和unordered_set的源码模拟实现
unordered_map和unordered_set的源码模拟实现
|
编译器 C++
【STL】模拟实现简易 list
【STL】模拟实现简易 list
29 0
|
存储 编译器 C++
一篇文章让你熟悉unordered_map及其模拟实现(上)
unordered_map的定义 哈希表在 C++ 标准库中的实现有一段历史。在 C++98/03 标准中,没有正式定义标准的哈希表容器。不
|
存储 算法 C++
一篇文章让你熟悉unordered_map及其模拟实现(下)
unordered_map哈希策略函数 1. load_factor float load_factor() const noexcept; load_factor 函数用于获取当前
|
存储 安全 C++
一篇文章让你熟悉unordered_set及其模拟实现(上)
unordered_set的定义 unordered_set 是 C++ 标准库中的一个容器,用于存储唯一的元素,而且不按照任何特定的顺序来组织这些元素。它是基于哈希表实现的,因此可以在平均情况下提供常数时间的插
一篇文章让你熟悉unordered_set及其模拟实现(下)
unordered_set修饰符函数 1. emplace template <class... Args> pair <iterator,bool> e
C++STL——map与set的模拟实现(上)
C++STL——map与set的模拟实现(上)