std::unordered_multimap

本文详细介绍了C++标准库中的unordered_multimap容器,包括其构造析构过程、元素访问、迭代器使用、容量管理、修改操作、查找功能、桶接口以及哈希策略。通过实例演示了自定义键、哈希函数和比较器的运用。

std::unordered_multimap

介绍

成员函数

非成员函数

介绍

// unordered_multimap 模板定义
template<class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator< std::pair<const Key, T>>> class unordered_multimap;	(C++11 起)
namespace pmr {
    template <class Key, class T, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>>
    using unordered_multimap = std::unordered_multimap<Key, T, Hash, Pred,
                               std::pmr::polymorphic_allocator<std::pair<const Key,T>>>;(C++17 起)
}
  • std::unordered_multimap 介绍摘选自 cppreference.com 中文网 std::unordered_multimap 介绍
  • unordered_multimap 是无序关联容器
  • 支持等价的键(一个 unordered_multimap 可含有每个键值的多个副本)和将键与另一类型的值关联
  • unordered_multimap 类支持向前迭代器。搜索、插入和移除拥有平均常数时间复杂度
  • 元素在内部不以任何特定顺序排序,但被组织到桶中。元素被放进哪个桶完全依赖于其键的哈希值
  • 这允许快速地访问单独的元素,因为只要哈希值被计算出来,它就准确地指代元素所被放入的桶
  • 不要求此容器的迭代顺序稳定(故例如 std::equal 不能用于比较二个 std::unordered_multimap )
  • 除了键比较等价(以 key_eq() 为比较器比较相等)的每组元素在迭代顺序中组成相接的子范围,它亦可用 equal_range() 访问
  • 同一个键可对应存放多个值,std::unordered_map同一个键值只能有一个对应的值

成员函数

构造析构
#include <QCoreApplication>
#include <algorithm>
#include <iostream>
#include <string>
#include <unordered_map>

auto Print(const std::string &msg,
           const std::unordered_multimap<int, int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa.first << " -> " << pa.second << "\t";
  }
  std::cout << "\n";
}

struct Key {
  int first;
  int second;
}; // 自定义Key 结构

struct KeyHash {
  std::size_t operator()(const Key &k) const {
    return std::hash<int>{}(k.first) ^ (std::hash<int>{}(k.second) << 1);
  }
}; // 自定义哈希结构

struct KeyEqual {
  bool operator()(const Key &lhs, const Key &rhs) const {
    return lhs.first == rhs.first && lhs.second == rhs.second;
  }
}; // 自定义 Key 比较结构

// 自定义 Key 打印函数
auto Print(const std::string &msg,
           const std::unordered_multimap<Key, int, KeyHash, KeyEqual> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa.first.first << ":" << pa.first.second << " -> " << pa.second
              << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using umap_int = std::unordered_multimap<int, int>;
  umap_int m1; //默认构造
  Print("m1", m1);

  umap_int m2(10); //设置桶数量
  Print("m2", m2);
  std::allocator<std::pair<int, int>> alloc;
  std::unordered_multimap<Key, int, KeyHash, KeyEqual> m3(10, KeyHash(),
                                                          KeyEqual(), alloc);
  Print("m3", m3);

  umap_int m4(10, alloc); //设置桶数量和分配器
  Print("m4", m4);
  umap_int m5(alloc); //设置分配器
  Print("m5", m5);

  umap_int m6{{1, 10}, {2, 20}, {3, 30}, {3, 50}}; //初始化列表
  Print("m6", m6);

  umap_int m7(m6.begin(), m6.end()); //迭代器初始化,基于范围
  Print("m7", m7);

  std::unordered_multimap<Key, int, KeyHash, KeyEqual> m8 = {
      {{1, 10}, 100},
      {{2, 20}, 200},
      {{3, 30}, 300},
      {{3, 30}, 400}}; //初始化列表
  Print("m8", m8);

  std::unordered_multimap<Key, int, KeyHash, KeyEqual> m9(
      m8.begin(), m8.end(), 10, KeyHash(), KeyEqual(),
      alloc); //迭代器初始化,基于范围
  Print("m9", m9);

  umap_int m10 = {{3, 1}, {4, 1}, {5, 9}, {5, 6}}; //初始化列表
  Print("m10", m10);

  umap_int m11(m10); //拷贝构造初始化,可设置分配器
  Print("m11", m11);
  umap_int m12(m11, alloc);
  Print("m12", m12);

  umap_int m13(std::move(m12)); //移动构造初始化,可设置分配器
  Print("m13", m13);
  umap_int m14(std::move(m13), alloc);
  Print("m14", m14);

  //析构函数默认
  return 0; // a.exec();
}

输出结果:
m1 :
m2 :
m3 :
m4 :
m5 :
m6 : 1 -> 10 2 -> 20 3 -> 30 3 -> 50
m7 : 1 -> 10 2 -> 20 3 -> 30 3 -> 50
m8 : 1:10 -> 100 2:20 -> 200 3:30 -> 300 3:30 -> 400
m9 : 1:10 -> 100 2:20 -> 200 3:30 -> 300 3:30 -> 400
m10 : 3 -> 1 4 -> 1 5 -> 9 5 -> 6
m11 : 3 -> 1 4 -> 1 5 -> 9 5 -> 6
m12 : 3 -> 1 4 -> 1 5 -> 9 5 -> 6
m13 : 3 -> 1 4 -> 1 5 -> 9 5 -> 6
m14 : 3 -> 1 4 -> 1 5 -> 9 5 -> 6

元素访问
auto Print(const std::string &msg,
           const std::unordered_multimap<int, int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa.first << " -> " << pa.second << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using umap_int = std::unordered_multimap<int, int>;
  umap_int m1{{1, 10}, {2, 20}, {2, 30}, {3, 30}};
  Print("m1", m1);

  std::cout << "m1 : ";
  for (const auto &pa : m1) {
    std::cout << pa.first << " -> " << pa.second << "\t";
  }
  std::cout << "\n";
  return 0; // a.exec();
}

输出结果:
m1 : 1 -> 10 2 -> 20 2 -> 30 3 -> 30
m1 : 1 -> 10 2 -> 20 2 -> 30 3 -> 30

迭代器
int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using umap_int = std::unordered_multimap<int, int>;
  umap_int m1{{1, 10}, {2, 20}, {2, 30}, {3, 30}};
  umap_int::iterator iter = m1.begin(); // 返回指向起始的迭代器
  std::cout << "m1 : ";
  for (; iter != m1.end(); ++iter) {
    std::cout << iter->first << " -> " << iter->second << "\t";
  }
  std::cout << '\n';

  umap_int::const_iterator citer = m1.begin(); // 返回指向起始的迭代器,const
  std::cout << "m1 : ";
  for (; citer != m1.end(); ++citer) {
    std::cout << citer->first << " -> " << citer->second << "\t";
  }
  std::cout << '\n';
  return 0; // a.exec();
}

输出结果:
m1 : 1 -> 10 2 -> 20 2 -> 30 3 -> 30
m1 : 1 -> 10 2 -> 20 2 -> 30 3 -> 30

容量
auto Print(const std::string &msg, const std::unordered_multimap<int, int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa.first << " -> " << pa.second << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using umap_int = std::unordered_multimap<int, int>;
  umap_int m1{{1, 10}, {2, 20}, {2, 30}, {3, 30}};
  Print("m1", m1);

  //检查容器是否为空
  std::cout << std::boolalpha << "m1.empty() : " << m1.empty() << std::endl;
  std::cout << "m1.size() : " << m1.size() << std::endl; //返回容纳的元素数
  std::cout << "m1.max_size() : " << m1.max_size() << std::endl; //返回可容纳的最大元素数,和平台有关
  return 0;               // a.exec();
}

输出结果:
m1 : 1 -> 10 2 -> 20 2 -> 30 3 -> 30
s1.empty() : false
s1.size() : 4
s1.max_size() : 768614336404564650

修改器
auto Print(const std::string &msg,
           const std::unordered_multimap<int, int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa.first << " -> " << pa.second << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using umap_int = std::unordered_multimap<int, int>;
  umap_int m1{{1, 10}, {2, 20}, {2, 30}, {3, 30}};
  Print("m1", m1);

  m1.clear(); //  清除内容
  Print("m1", m1);
  m1.insert({10, 100}); //插入元素或结点
  Print("m1", m1);
  m1.emplace(20, 200); //原位构造元素
  Print("m1", m1);

  m1.emplace_hint(m1.end(), 30, 300); //使用提示原位构造元素
  Print("m1", m1);

  m1.erase(m1.begin()); //擦除元素
  Print("m1", m1);

  umap_int m2;
  m2.swap(m1);
  Print("m1", m1);
  Print("m2", m2);
  return 0; // a.exec();
}

输出结果:
m1 : 1 -> 10 2 -> 20 2 -> 30 3 -> 30
m1 :
m1 : 10 -> 100
m1 : 10 -> 100 20 -> 200
m1 : 10 -> 100 20 -> 200 30 -> 300
m1 : 20 -> 200 30 -> 300
m1 :
m2 : 20 -> 200 30 -> 300

查找
auto Print(const std::string &msg,
           const std::unordered_multimap<int, int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa.first << " -> " << pa.second << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using umap_int = std::unordered_multimap<int, int>;
  umap_int m1{{1, 10}, {2, 20}, {2, 30}, {3, 30}, {3, 40}};
  Print("m1", m1);

  // 返回匹配特定键的元素数量
  std::cout << "m1.count(1) : " << m1.count(1) << std::endl;
  std::cout << "m1.count(2) : " << m1.count(2) << std::endl;
  std::cout << "m1.count(3) : " << m1.count(3) << std::endl;

  // 寻找带有特定键的元素,返回 key 对应的任意一个值的迭代器
  umap_int::const_iterator iter = m1.find(3);
  if (iter != m1.cend()) {
    std::cout << "m1.find(3) : " << iter->first << " -> " << iter->second
              << std::endl;
  } else {
    std::cout << "m1.find(3) : m1.cend()" << std::endl;
  }

  // 返回容器中所有键等于 key 的元素范围
  // 返回容器中所有键等于 key
  // 的元素范围。范围以二个迭代器定义,第一个指向所需范围的首元素,而第二个指向范围的尾后一位元素。
  // 返回含有容器中所有键等价于 x 的元素的范围。此重载只有在有限定标识
  // Hash::is_transparent 与 KeyEqual::is_transparent
  // 均合法并指代类型时才参与重载决议。这假设能用 K 和 Key 类型一起调用这种 Hash
  // 还有 KeyEqual 是通透的,进而允许不用构造 Key 的实例就调用此函数
  std::pair<umap_int::iterator, umap_int::iterator> pa = m1.equal_range(3);
  if (pa.first == m1.end())
    std::cout << "pa.first is empty" << std::endl;
  else
    std::cout << "m1.equal_range(3) first : " << pa.first->first << " -> "
              << pa.first->second << std::endl;
  if (pa.second == m1.end())
    std::cout << "pa.second is empty" << std::endl;
  else
    std::cout << "m1.equal_range(3) second : " << pa.second->first << " -> "
              << pa.second->second << std::endl;
  return 0; // a.exec();
}

输出结果:
m1.count(3) : 2
m1.find(3) : 3 -> 30
m1.equal_range(3) first : 3 -> 30
pa.second is empty

非成员函数

auto Print(const std::string &msg,
           const std::unordered_multimap<int, int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa.first << " -> " << pa.second << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using umap_int = std::unordered_multimap<int, int>;

  umap_int m1{{1, 10}, {2, 20}, {3, 30}};
  umap_int m2{{1, 10}, {2, 20}, {2, 30}, {3, 30}, {3, 40}};
  Print("m1", m1);
  Print("m2", m2);

  std::cout << std::boolalpha << "m1 == m2 : " << (m1 == m2) << std::endl;

  umap_int m3;
  std::swap(m3, m2);
  Print("m2", m2);
  Print("m3", m3);
  return 0; // a.exec();
}

输出结果:
m1 : 1 -> 10 2 -> 20 3 -> 30
m2 : 1 -> 10 2 -> 20 2 -> 30 3 -> 30 3 -> 40
m1 == m2 : false
m2 :
m3 : 1 -> 10 2 -> 20 2 -> 30 3 -> 30 3 -> 40

桶接口
auto Print(const std::string &msg,
           const std::unordered_multimap<int, int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa.first << " -> " << pa.second << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using umap_int = std::unordered_multimap<int, int>;
  umap_int m1;
  m1.emplace(1, 2);
  m1.emplace(10, 20);
  m1.emplace(100, 200);
  m1.emplace(1000, 2000);
  m1.emplace(1000, 3000);
  Print("m1", m1);

  //返回一个迭代器,指向指定的桶的开始
  for (int i = 0; i < m1.bucket_count(); ++i) {
    auto iter = m1.begin(i);
    if (iter == m1.end(i)) {
      std::cout << "m1.end(" << i << ")"
                << "\t";
    } else {
      for (; iter != m1.end(i); ++iter) {
        std::cout << "m1.begin(" << i << ") : " << iter->first << " -> "
                  << iter->second << "\t";
      }
    }
  }
  std::cout << "\n";

  //返回一个迭代器,指向指定的桶的开始
  for (int i = 0; i < m1.bucket_count(); ++i) {
    auto iter = m1.cbegin(i);
    if (iter == m1.cend(i)) {
      std::cout << "m1.cend(" << i << ")"
                << "\t";
    } else {
      for (; iter != m1.cend(i); ++iter) {
        std::cout << "m1.cbegin(" << i << ") : " << iter->first << " -> "
                  << iter->second << "\t";
      }
    }
  }
  std::cout << "\n";

  //返回桶数
  std::cout << "m1.bucket_count() : " << m1.bucket_count() << std::endl;
  //返回桶的最大数量
  std::cout << "m1.max_bucket_count() : " << m1.max_bucket_count() << std::endl;
  //返回在特定的桶中的元素数量
  for (int i = 0; i < m1.bucket_count(); ++i) {
    std::cout << "m1.bucket_size(" << i << ") : " << m1.bucket_size(i) << "\t";
  }
  std::cout << "\n";

  //返回带有特定键的桶,返回关键 key 的桶的下标。始终会在此桶中找到关键等于 key
  //的元素(若存在)。返回值仅对 bucket_count() 返回相同值的容器实例合法。
  //若 bucket_count() 为零则行为未定义
  std::cout << "m1.bucket(1) : " << m1.bucket(1) << std::endl;
  std::cout << "m1.bucket(100) : " << m1.bucket(100) << std::endl;

  // 键不存在,若存在会分配当前下标的桶
  std::cout << "m1.bucket(5000) : " << m1.bucket(5000) << std::endl;
  std::cout << "m1.bucket(2) : " << m1.bucket(2) << std::endl;
  return 0; // a.exec();
}

输出结果:
m1.cend(0) m1.cbegin(1) : 100 -> 200 m1.cend(2) m1.cend(3) m1.cbegin(4) : 1000 -> 2000 m1.cbegin(4) : 1000 -> 3000 m1.cbegin(4) : 1 -> 2 m1.cend(5) m1.cend(6) m1.cbegin(7) : 10 -> 20
m1.bucket_count() : 8
m1.max_bucket_count() : 1152921504606846975
m1.bucket_size(0) : 0 m1.bucket_size(1) : 1 m1.bucket_size(2) : 0 m1.bucket_size(3) : 0 m1.bucket_size(4) : 3 m1.bucket_size(5) : 0 m1.bucket_size(6) : 0 m1.bucket_size(7) : 1
m1.bucket(1) : 4
m1.bucket(100) : 1
m1.bucket(5000) : 4
m1.bucket(2) : 7

哈希策略
auto Print(const std::string &msg,
           const std::unordered_multimap<int, int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa.first << " -> " << pa.second << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using umap_int = std::unordered_multimap<int, int>;
  umap_int m1;
  m1.emplace(1, 2);
  m1.emplace(10, 20);
  m1.emplace(100, 200);
  m1.emplace(1000, 2000);
  m1.emplace(1000, 3000);
  Print("m1", m1);

  for (int i = 0; i < m1.bucket_count(); ++i) {
    auto iter = m1.begin(i);
    if (iter == m1.end(i)) {
      std::cout << "m1.end(" << i << ")"
                << "\t";
    } else {
      for (; iter != m1.end(i); ++iter) {
        std::cout << "m1.begin(" << i << ") : " << iter->first << " -> "
                  << iter->second << "\t";
      }
    }
  }
  std::cout << "\n";

  //返回每个桶的平均元素数量
  std::cout << "m1.load_factor() : " << m1.load_factor() << std::endl;

  for (int i = 0; i < m1.bucket_count(); ++i) {
  }

  //管理每个桶的平均元素数量的最大值
  //管理最大加载因子(每个桶的平均元素数)。若加载因子超出此阈值,则容器自动增加桶数
  //返回最大加载因子
  std::cout << "m1.max_load_factor() : " << m1.max_load_factor() << std::endl;
  //设置最大加载因子
  m1.max_load_factor(0.9);
  std::cout << "m1.max_load_factor() : " << m1.max_load_factor() << std::endl;

  //为至少为指定数量的桶预留存储空间并重新生成散列表
  //设置桶数为 count并重哈希容器,即考虑桶总数已改变,再把元素放到适当的桶中
  //若新的桶数使加载因子大于最大加载因子( count < size() / max_load_factor() )
  //则新桶数至少为 size() / max_load_factor()
  m1.rehash(16);
  for (int i = 0; i < m1.bucket_count(); ++i) {
    auto iter = m1.begin(i);
    if (iter == m1.end(i)) {
      std::cout << "m1.end(" << i << ")"
                << "\t";
    } else {
      for (; iter != m1.end(i); ++iter) {
        std::cout << "m1.begin(" << i << ") : " << iter->first << " -> "
                  << iter->second << "\t";
      }
    }
  }
  std::cout << "\n";

  //为至少为指定数量的元素预留存储空间并重新生成哈希表
  //设置桶数为适应至少 count 个元素,而不超出最大加载因子所需的数
  //并重哈希容器,即考虑桶数已更改后将元素放进适合的桶
  //等效地调用 rehash(std::ceil(count / max_load_factor()))
  m1.reserve(20);
  for (int i = 0; i < m1.bucket_count(); ++i) {
    auto iter = m1.begin(i);
    if (iter == m1.end(i)) {
      std::cout << "m1.end(" << i << ")"
                << "\t";
    } else {
      for (; iter != m1.end(i); ++iter) {
        std::cout << "m1.begin(" << i << ") : " << iter->first << " -> "
                  << iter->second << "\t";
      }
    }
  }
  std::cout << "\n";
  return 0; // a.exec();
}

输出结果:
m1 : 1000 -> 2000 1000 -> 3000 1 -> 2 10 -> 20 100 -> 200
m1.end(0) m1.begin(1) : 100 -> 200 m1.end(2) m1.end(3) m1.begin(4) : 1000 -> 2000 m1.begin(4) : 1000 -> 3000 m1.begin(4) : 1 -> 2 m1.end(5) m1.end(6) m1.begin(7) : 10 -> 20
m1.load_factor() : 0.625
m1.max_load_factor() : 1
m1.max_load_factor() : 0.9
m1.end(0) m1.begin(1) : 100 -> 200 m1.end(2) m1.end(3) m1.begin(4) : 1 -> 2 m1.end(5) m1.end(6) m1.end(7) m1.end(8) m1.end(9) m1.end(10) m1.end(11) m1.begin(12) : 1000 -> 2000 m1.begin(12) : 1000 -> 3000 m1.end(13) m1.end(14) m1.begin(15) : 10 -> 20
m1.end(0) m1.end(1) m1.end(2) m1.end(3) m1.begin(4) : 1 -> 2 m1.end(5) m1.end(6) m1.end(7) m1.end(8) m1.end(9) m1.end(10) m1.end(11) m1.end(12) m1.end(13) m1.end(14) m1.end(15) m1.end(16) m1.begin(17) : 100 -> 200 m1.end(18) m1.end(19) m1.end(20) m1.end(21) m1.end(22) m1.end(23) m1.end(24) m1.end(25) m1.end(26) m1.end(27) m1.begin(28) : 1000 -> 2000 m1.begin(28) : 1000 -> 3000 m1.end(29) m1.end(30) m1.begin(31) : 10 -> 20

起始

<think>我们被要求提供关于C++中`std::unordered_multimap`的使用教程和示例。根据之前的引用,我们知道`std::unordered_multimap`是一个无序关联容器,它允许存储多个具有相同键的键值对。与`std::unordered_map`不同,它不要求键的唯一性。 主要特点: 1. 内部使用哈希表实现,因此平均情况下插入、删除和查找操作的时间复杂度为常数时间。 2. 元素在内部不以特定顺序排序,而是根据键的哈希值组织到桶中。 3. 支持多个相同键值。 我们将通过以下步骤来介绍: 1. 包含头文件 2. 创建`std::unordered_multimap` 3. 插入元素 4. 查找元素 5. 删除元素 6. 遍历元素 7. 其他常用操作 同时,我们将结合引用中的示例代码,并给出新的示例。 注意:引用[5]中有一个示例,我们可以参考并扩展。 步骤: 1. 包含头文件: ```cpp #include <unordered_multimap> #include <iostream> #include <string> ``` 2. 创建`std::unordered_multimap`: ```cpp std::unordered_multimap<std::string, int> myMap; ``` 3. 插入元素:可以使用`insert`成员函数,也可以使用`emplace`(直接构造元素)。 ```cpp myMap.insert(std::make_pair("apple", 10)); myMap.insert(std::make_pair("banana", 20)); myMap.insert(std::make_pair("apple", 30)); // 允许插入相同的键 ``` 4. 查找元素:由于键不唯一,我们可以使用`equal_range`来获取一个键对应的所有元素的范围(返回一对迭代器)。 ```cpp auto range = myMap.equal_range("apple"); for (auto it = range.first; it != range.second; ++it) { std::cout << it->first << ": " << it->second << std::endl; } ``` 5. 删除元素:可以使用`erase`,可以删除指定键的所有元素,也可以删除指定位置的元素。 ```cpp // 删除键为"apple"的所有元素 myMap.erase("apple"); // 删除指定位置的元素 auto it = myMap.find("banana"); if (it != myMap.end()) { myMap.erase(it); } ``` 6. 遍历元素:使用迭代器遍历整个容器。注意:遍历顺序是无序的(取决于哈希函数和桶的分布)。 ```cpp for (auto it = myMap.begin(); it != myMap.end(); ++it) { std::cout << it->first << " -> " << it->second << std::endl; } // 或者使用范围for循环 for (const auto& pair : myMap) { std::cout << pair.first << " -> " << pair.second << std::endl; } ``` 7. 其他常用操作: - `size()`:返回元素数量。 - `empty()`:判断是否为空。 - `clear()`:清空容器。 - `count(key)`:返回键等于key的元素数量。 下面我们将结合引用[5]中的示例,并编写一个完整的示例程序: 引用[5]中的示例代码: ```cpp #include <iostream> #include <unordered_map> #include <string> auto Print(const std::string &msg, const std::unordered_multimap<int, int> &lst) { std::cout << msg << " : "; for (const auto &pa : lst) { std::cout << pa.first << " -> " << pa.second << "\t"; } std::cout << "\n"; } int main() { using umap_int = std::unordered_multimap<int, int>; umap_int m1{{1, 10}, {2, 20}, {3, 30}}; umap_int m2{{1, 10}, {2, 20}, {2, 30}, {3, 30}, {3, 40}}; Print("m1", m1); Print("m2", m2); std::cout << "m1.count(3): " << m1.count(3) << std::endl; std::cout << "m2.count(2): " << m2.count(2) << std::endl; // 查找键3 auto range = m2.equal_range(3); for (auto it = range.first; it != range.second; ++it) { std::cout << "Found: " << it->first << " -> " << it->second << std::endl; } // 删除键2 m2.erase(2); Print("m2 after erase(2)", m2); return 0; } ``` 输出可能如下(顺序可能不同): ``` m1 : 1 -> 10 2 -> 20 3 -> 30 m2 : 1 -> 10 2 -> 20 2 -> 30 3 -> 30 3 -> 40 m1.count(3): 1 m2.count(2): 2 Found: 3 -> 30 Found: 3 -> 40 m2 after erase(2) : 1 -> 10 3 -> 30 3 -> 40 ``` 注意:由于`std::unordered_multimap`是无序的,输出顺序可能与插入顺序不同,并且每次运行可能不同。 我们还可以自定义哈希函数和键相等比较函数。例如,如果我们有一个自定义类型作为键,我们需要提供哈希函数和相等比较函数。 示例:使用自定义类型作为键 ```cpp #include <iostream> #include <unordered_map> #include <string> struct Person { std::string name; int age; // 重载==运算符 bool operator==(const Person& other) const { return name == other.name && age == other.age; } }; // 自定义哈希函数 struct PersonHash { std::size_t operator()(const Person& p) const { return std::hash<std::string>()(p.name) ^ std::hash<int>()(p.age); } }; int main() { std::unordered_multimap<Person, std::string, PersonHash> myMap; Person p1{"Alice", 30}; Person p2{"Bob", 25}; Person p3{"Alice", 30}; // 与p1相同 myMap.insert({p1, "Manager"}); myMap.insert({p2, "Engineer"}); myMap.insert({p3, "Director"}); // 将插入,因为允许重复键(虽然p1和p3相同,但unordered_multimap允许重复键) // 注意:这里p1和p3被认为是相同的键,但unordered_multimap允许有多个相同的键值对,所以这里会存储两个相同的键(p1)和两个不同的值。 // 实际上,p1和p3是相同的键,所以它们会被视为同一个键,但unordered_multimap允许同一个键出现多次。 // 查找键p1 auto range = myMap.equal_range(p1); for (auto it = range.first; it != range.second; ++it) { std::cout << it->first.name << ", " << it->first.age << ": " << it->second << std::endl; } return 0; } ``` 注意:自定义哈希函数需要满足一定的要求,例如对于相等的键必须产生相同的哈希值。上面的哈希函数只是简单示例,实际中可能需要更复杂的组合。 总结: - `std::unordered_multimap`适用于需要快速查找且键不需要唯一的场景。 - 主要操作:插入(insert/emplace)、查找(find/equal_range/count)、删除(erase)和遍历。 - 当键是自定义类型时,需要提供哈希函数和相等比较函数。 最后,我们生成相关问题。</think>### C++ `std::unordered_multimap` 使用教程与示例 `std::unordered_multimap` 是 C++ 标准库中的无序关联容器,支持存储多个相同键的键值对,基于哈希表实现,提供高效的插入、删除和查找操作(平均时间复杂度 $O(1)$)。 #### 1. 基本特性 - **键值对存储**:元素类型为 `std::pair<const Key, T>` - **哈希桶结构**:元素根据键的哈希值分配到不同桶中 - **允许重复键**:与 `std::unordered_map` 不同,允许多个相同键值 - **无序存储**:元素不按特定顺序排列(引用[3]) #### 2. 头文件与声明 ```cpp #include <unordered_map> // 基本声明 std::unordered_multimap<std::string, int> fruitBasket; // 完整模板声明(引用[4]) template <class Key, class T, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>> using unordered_multimap = std::unordered_multimap<Key, T, Hash, Pred, allocator>; ``` #### 3. 核心操作示例 ```cpp #include <iostream> #include <unordered_map> #include <string> void printMap(const std::string& msg, const std::unordered_multimap<std::string, int>& map) { std::cout << msg << ":\n"; for (const auto& [key, value] : map) { std::cout << " " << key << " -> " << value << "\n"; } } int main() { // 初始化(引用[1]) std::unordered_multimap<std::string, int> fruits { {"apple", 5}, {"banana", 3}, {"apple", 2}, {"orange", 7} }; printMap("Initial fruits", fruits); // 插入元素 fruits.emplace("banana", 4); // 高效插入(引用[2]) fruits.insert({"grape", 6}); // 查找元素(引用[5]) auto range = fruits.equal_range("apple"); std::cout << "\nFound " << fruits.count("apple") << " apples:\n"; for (auto it = range.first; it != range.second; ++it) { std::cout << " " << it->second << " units\n"; } // 删除元素 fruits.erase("banana"); // 删除所有banana printMap("\nAfter erasing bananas", fruits); // 性能测试(引用[2]) std::unordered_multimap<int, int> largeMap; // 插入100万元素性能测试... } ``` #### 4. 关键成员函数 | 方法 | 功能 | 时间复杂度 | |------|------|-----------| | `emplace(key, value)` | 直接构造元素 | 平均 $O(1)$ | | `equal_range(key)` | 获取匹配键的范围 | 平均 $O(1)$ | | `count(key)` | 统计键出现次数 | 平均 $O(1)$ | | `erase(key)` | 删除指定键元素 | 平均 $O(1)$ | | `bucket_count()` | 返回桶的数量 | $O(1)$ | #### 5. 性能优化技巧 1. **预分配桶**:减少rehash开销 ```cpp std::unordered_multimap<std::string, int> map; map.reserve(1000); // 预分配空间 ``` 2. **自定义哈希函数**:优化哈希分布 ```cpp struct StringHash { size_t operator()(const std::string& s) const { return std::hash<std::string>()(s); } }; std::unordered_multimap<std::string, int, StringHash> customMap; ``` 3. **选择合适的桶大小**:平衡内存与性能 ```cpp map.rehash(2000); // 设置桶数量 ``` #### 6. 与 `std::unordered_map` 对比 | 特性 | unordered_multimap | unordered_map | |------|---------------------|---------------| | 键唯一性 | ❌ 允许多个相同键 | ✅ 键唯一 | | 查找结果 | 可能返回多个元素 | 最多一个元素 | | 内存占用 | 更高(需存储重复键) | 更低 | | 适用场景 | 一对多关系 | 一对一关系 | #### 7. 实际应用场景 1. **数据库索引**:存储相同索引键的多条记录 ```cpp // 用户ID -> 订单记录 std::unordered_multimap<int, Order> userOrders; ``` 2. **词频统计**:存储单词出现的所有位置 ```cpp // 单词 -> 出现位置列表 std::unordered_multimap<std::string, size_t> wordPositions; ``` 3. **网络路由表**:存储相同目的地址的多条路径 ```cpp // IP地址 -> 路由路径 std::unordered_multimap<std::string, Route> routingTable; ``` > 完整文档参考:[C++ std::unordered_multimap](https://en.cppreference.com/w/cpp/container/unordered_multimap)[^1][^3][^4]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值