自定义unordered_map、unordered_set的hash function、compare function

本文详细介绍了在C++ STL中,如何为自定义类型如`Person`在`unordered_map`和`unordered_set`中添加自定义的hashfunction和comparefunction。自定义hashfunction用于计算对象的哈希值,而comparefunction则用于精确比较对象。文中通过两种方式定义hash和equal函数,并给出了完整的示例程序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}

### Java 中实现无序双键复合键的方法 在某些场景下,我们需要设计一种数据结构或者机制来支持由两个键组成的复合键,并且这两个键的顺序不影响其唯一性。例如,在数据库映射或缓存管理中,这种需求较为常见。以下是几种常见的解决方案及其具体实现方式: #### 1. **自定义类并重写 `equals` 和 `hashCode` 方法** 通过创建一个新的类来封装两个键,并在此基础上覆盖 `equals` 和 `hashCode` 方法以确保它们的行为符合预期。 ```java import java.util.Objects; public class UnorderedPair<K> { private K key1; private K key2; public UnorderedPair(K key1, K key2) { this.key1 = key1; this.key2 = key2; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof UnorderedPair)) return false; UnorderedPair<?> that = (UnorderedPair<?>) o; // Compare keys without considering order. return Objects.equals(key1, that.key1) && Objects.equals(key2, that.key2) || Objects.equals(key1, that.key2) && Objects.equals(key2, that.key1); } @Override public int hashCode() { // Generate hash code based on sorted pair of keys to ensure consistency regardless of insertion order. int result = 0; if (key1 != null || key2 != null) { Comparable[] comparableKeys = new Comparable[]{key1, key2}; java.util.Arrays.sort(comparableKeys); result += comparableKeys[0].hashCode(); if (comparableKeys[1] != null) { result ^= comparableKeys[1].hashCode(); // XOR ensures unordered nature is preserved in the hash function as well. } } return result; } } ``` 这段代码展示了一个名为 `UnorderedPair` 的泛型类,它接受任意类型的两个键作为参数。为了使该类能够在诸如 `HashMap` 或其他基于哈希的数据结构中正常工作,我们不仅需要正确地实现 `equals` 方法以便比较时忽略键的顺序差异[^1],还需要精心构造 `hashCode` 方法使得即使交换了两者的相对位置也不会改变整体的散列值[^2]。 #### 2. **利用 Set 数据结构的特点** 另一种更简洁的方式是借助不可变集合(Immutable Collection),比如 Guava 库里的 `ImmutableSet` 或者标准库中的 `HashSet` 来自动处理重复性和次序无关性的特性。 ```java import com.google.common.collect.ImmutableSet; // Assuming you have added dependency for Google's Guava library. public static ImmutableSet<Object> createCompoundKey(Object k1, Object k2){ return ImmutableSet.of(k1,k2); } // Usage Example: Map<ImmutableSet<String>, String> mapWithCompoundKeys = Maps.newHashMap(); mapWithCompoundKeys.put(createCompoundKey("A", "B"), "ValueAB"); System.out.println(mapWithCompoundKeys.get(createCompoundKey("B","A"))); // Outputs: ValueAB ``` 在这里,由于 `Set` 天然具备不关心元素排列特性的优势,因此无需额外费心去调整我们的业务逻辑就能轻松达成目的[^3]。不过需要注意的是引入第三方依赖可能会增加项目的复杂度以及维护成本,请根据实际情况权衡利弊后再做决定。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值