一个有趣的用途是为给定对象集定义内存高效的索引(数据库术语)。
示例
假设我们有一个程序,其中包含一个由这个类的N
个对象组成的集合:
struct Person {
std::string firstName;
std::string lastName;
std::string socialSecurityNumber;
std::string email;
}
我们需要通过任何唯一属性的值高效地检索对象。
实现比较
时间复杂度是在字符串比较为常数时间(字符串长度有限)的情况下给出的。
1)单个unordered_map
使用单个键(例如email
)索引的单个map
:
最初的回答
std::unordered_map<std::string,Person> indexedByEmail
- 时间复杂度:查找除
email
以外的任何唯一属性都需要遍历映射表:平均O(N)。
- 内存使用:重复使用
email
值。这可以通过使用具有自定义哈希和比较的单个set
来避免(请参见3)。
2) 多个unordered_map
,没有自定义哈希和比较
对于每个唯一属性都有一个映射表,并且使用默认哈希和比较:
std::unordered_map<std::pair<std::string,std::string>, Person> byName
std::unordered_map<std::string, const Person*> byEmail
std::unordered_map<std::string, const Person*> bySSN
- 时间复杂度:通过使用适当的映射,任何唯一属性的查找平均为O(1)。
- 内存使用:低效,因为所有字符串都会被复制。
3) 多个unordered_set
,自定义哈希和比较:
通过自定义哈希和比较,我们可以定义不同的unordered_set
,它们只会对对象的特定字段进行哈希和比较。这些集合可以用来执行查找,就像在2中存储项目一样,但不会复制任何字段。
"Original Answer" 翻译成 "最初的回答"
using StrHash = std::hash<std::string>;
struct PersonNameHash {
std::size_t operator()(const Person& p) const {
return StrHash()(p.firstName) + StrHash()(p.lastName);
}
};
struct PersonNameEqual {
bool operator()(const Person& p1, const Person& p2) const {
return (p1.firstName == p2.firstName) && (p1.lastName == p2.lastName);
}
};
std::unordered_set<Person, PersonNameHash, PersonNameEqual> byName;
struct PersonSsnHash {
std::size_t operator()(const Person* p) const {
return StrHash()(p->socialSecurityNumber);
}
};
struct PersonSsnEqual {
bool operator()(const Person* p1, const Person* p2) const {
return p1->socialSecurityNumber == p2->socialSecurityNumber;
}
};
std::unordered_set<const Person*, PersonSsnHash, PersonSsnEqual> bySSN;
struct PersonEmailHash {
std::size_t operator()(const Person* p) const {
return StrHash()(p->email);
}
};
struct PersonEmailEqual {
bool operator()(const Person* p1, const Person* p2) const {
return p1->email == p2->email;
}
};
std::unordered_set<const Person*,PersonEmailHash,PersonEmailEqual> byEmail;
- 时间复杂度:通过任何唯一属性进行查找仍然是平均O(1)。
- 内存使用:比2)要好得多:没有
string
重复。
现场演示
{a,b}
的哈希函数是std::hash<float>(static_cast<float>(a)/b)
。为什么在哈希之前需要将{42, 63}
转换为{2,3}
? - JaMiT