1.问题
C++ STL容器类unordered_map、unordered_set使用起来非常方便,但当向容器中添加自定义类型的元素时,需提供自定义的hash function、compare function,原因:C++ STL只提供了基本类型(如int、string等)的hash function、compare function,其中,自定义hash function用于计算自定义类型对象的hash value,但由于hash function存在碰撞现象,所以自定义compare function用于精准地比较两个自定义类型对象是否相同
unordered_map、unordered_set实现均基于哈希表,所以外部接口相似,这里以unordered_set举例,本博客中的自定义类型Person如下所示:
struct Person {
string name_;
int age_;
Person(string name, int age) {
name_ = name;
age_ = age;
}
};
2.指明hash function的方式
1)将自定义hash function注入到std空间中,这种方式会自动注入unordered_set<Person>中,不用在声明使用unodered_set时再次指明
namespace std {
template <>
struct hash<Person> {
size_t operator()(const Person& person) const {
size_t res = 17;
res = res * 31 + hash<string>()(person.name_);
res = res * 31 + hash<int>()(person.age_);
return res;
}
};
};
2)将自定义hash function放到一个单独的struct(或class)中,需要在unodered_set中指明承载hash function的struct(或class)
struct PersonHasher1 {
size_t operator()(const Person& person) const {
size_t res = 17;
res = res * 31 + hash<string>()(person.name_);
res = res * 31 + hash<int>()(person.age_);
return res;
}
};
或
class PersonHasher2 {
public:
size_t operator()(const Person& person) const {
size_t res = 17;
res = res * 31 + hash<string>()(person.name_);
res = res * 31 + hash<int>()(person.age_);
return res;
}
};
3.指明compare function的方式
1)在Person结构体中添加重载运算符operator==
struct Person {
string name_;
int age_;
Person(string name, int age) {
name_ = name;
age_ = age;
}
bool operator==(const Person& other) const {
return (name_ == other.name_) && (age_ == other.age_);
}
};
2)将自定义compare function放到一个单独的struct(或class)中,需要在unodered_set中指明承载compare function的struct(或class)
struct PersonEqual1 {
bool operator()(const Person& person1, const Person& person2) const {
return (person1.name_ == person2.name_) && (person1.age_ == person2.age_);
}
};
或
class PersonEqual2 {
public:
bool operator()(const Person& person1, const Person& person2) const {
return (person1.name_ == person2.name_) && (person1.age_ == person2.age_);
}
};
4.完整的示例程序
#include <iostream>
#include <unordered_set>
using namespace std;
struct Person {
string name_;
int age_;
Person(string name, int age) {
name_ = name;
age_ = age;
}
bool operator==(const Person& other) const {
return (name_ == other.name_) && (age_ == other.age_);
}
};
namespace std {
template <>
struct hash<Person> {
size_t operator()(const Person& person) const {
size_t res = 17;
res = res * 31 + hash<string>()(person.name_);
res = res * 31 + hash<int>()(person.age_);
return res;
}
};
};
struct PersonHasher1 {
size_t operator()(const Person& person) const {
size_t res = 17;
res = res * 31 + hash<string>()(person.name_);
res = res * 31 + hash<int>()(person.age_);
return res;
}
};
class PersonHasher2 {
public:
size_t operator()(const Person& person) const {
size_t res = 17;
res = res * 31 + hash<string>()(person.name_);
res = res * 31 + hash<int>()(person.age_);
return res;
}
};
struct PersonEqual1 {
bool operator()(const Person& person1, const Person& person2) const {
return (person1.name_ == person2.name_) && (person1.age_ == person2.age_);
}
};
class PersonEqual2 {
public:
bool operator()(const Person& person1, const Person& person2) const {
return (person1.name_ == person2.name_) && (person1.age_ == person2.age_);
}
};
int main() {
// unordered_set<Person, PersonHasher2, PersonEqual2> persons;
unordered_set<Person> persons;
Person person("zhangsan", 11);
persons.emplace(person);
for(auto item : persons) {
cout << item.name_ << " " << item.age_ << endl;
}
return 0;
}