【问题标题】:HashMap implementation that allows to override hash and equals methodHashMap 实现,允许覆盖 hash 和 equals 方法
【发布时间】:2017-01-04 10:25:36
【问题描述】:

在 Java 中,您可以覆盖 HashMap 的键对象的 equals 和 hash 方法,以确定如何生成哈希码以及何时应将两个键对象视为相等。

是否有任何 Map 实现允许在 Map 类中通过覆盖其哈希方法(并通过可覆盖的 equals(key1,key2) 方法确定键相等性)来定义哈希和等于?

用例:

Ĺets 假设我们有一个 GeoData 类的对象,其中包含以下字段:国家、地区、城市。我们要访问两个地图:地图x存储每个区域的居民数量,地图y存储每个区域的居民数量每个城市的居民。

要获取 GeoData 对象的两个信息,我们首先需要从该对象中提取国家和地区,然后创建一个 X 类的新对象,该对象定义哈希和等于考虑国家和地区,并将其用作地图的键 x。此外,我们需要对国家、地区和城市做同样的事情。创建一个 Y 类的新对象并使用它从地图 y 中获取值。

如果我们为这些地图 xy 中的每一个都扩展一个 Map 实现会不会更容易> 并覆盖 hash(GeoData key) 和 equals(GeoData key1,GeoData key2) 方法,这样也可以避免为每次访问创建新的 key 对象?

hash(GeoData key) 可以在地图 x 中使用国家和地区,或者在地图 y 中使用国家、地区和城市em> 用于哈希码计算。

更新:

已正确标记为重复。 This 回答建议 apache commons-collections AbstractHashMap 是我正在寻找的。​​p>

【问题讨论】:

    标签: java hashmap


    【解决方案1】:

    如果我理解正确,您想在不同的Maps 中使用具有不同相等标准的相同密钥类型 (GeoData)。

    如果您使用 TreeMap 而不是 HashMap 并将不同的 Comparator<GeoData> 传递给每个 TreeMap 构造函数,则可以这样做(通过比较国家、地区和城市以及other 只会比较国家和地区)。

    【讨论】:

      【解决方案2】:

      您还可以为地图创建装饰器。它不会避免为键创建一个新类,但它会将它隐藏起来,因此映射的类型为Map<GeoData, Integer>。当然,如果您执行keySet() 之类的操作,则 city 必须始终为空,因为此信息会丢失(它不是密钥的一部分)。

      public class Country {}
      public class Region {}
      public class City {}
      
      public class GeoData {
      
          public final Country country;
          public final Region region;
          public final City city;
      
          public GeoData(Country country, Region region, City city) {
              this.country = country;
              this.region = region;
              this.city = city;
          }
      }
      
      public class InhabitantsInRegion  implements Map<GeoData, Integer> {
      
          private static class InnerKey {
      
              private final Country country;
              private final Region region;
      
              private InnerKey(Country country, Region region) {
                  this.country = country;
                  this.region = region;
              }
      
              // hashcode & equals
          }
      
          private final Map<InnerKey, Integer> map = new HashMap<>();
      
          @Override
          public int size() {
              return map.size();
          }
      
          @Override
          public boolean isEmpty() {
              return map.isEmpty();
          }
      
          @Override
          public boolean containsKey(Object key) {
              return key instanceof GeoData && map.containsKey(newInnerKey((GeoData) key));
          }
      
          @Override
          public boolean containsValue(Object value) {
              return map.containsValue(value);
          }
      
          @Override
          public Integer get(Object key) {
              if (key instanceof GeoData) {
                  return map.get(newInnerKey((GeoData) key));
              }
              return null;
          }
      
          @Override
          public Integer put(GeoData key, Integer value) {
              return map.put(new InnerKey(key.country, key.region), value);
          }
      
          @Override
          public void putAll(Map<? extends GeoData, ? extends Integer> m) {
              m.entrySet().forEach(entry -> put(entry.getKey(), entry.getValue()));
          }
      
          @Override
          public Integer remove(Object key) {
              if (key instanceof GeoData) {
                  return map.remove(newInnerKey((GeoData) key));
              }
              return null;
          }
      
          @Override
          public void clear() {
              map.clear();
          }
      
          @Override
          public Set<GeoData> keySet() {
              return map.keySet().stream()
                      .map(InhabitantsInRegion::newGeoDataKey)
                      .collect(toSet());
          }
      
          @Override
          public Collection<Integer> values() {
              return map.values();
          }
      
          @Override
          public Set<Entry<GeoData, Integer>> entrySet() {
              return map.entrySet().stream()
                      .map(this::newEntry)
                      .collect(toSet());
          }
      
      
      
          private Entry<GeoData, Integer> newEntry(Entry<InnerKey, Integer> entry) {
              return new Entry<GeoData, Integer>() {
                  @Override
                  public GeoData getKey() {
                      return newGeoDataKey(entry.getKey());
                  }
      
                  @Override
                  public Integer getValue() {
                      return entry.getValue();
                  }
      
                  @Override
                  public Integer setValue(Integer value) {
                      return map.put(entry.getKey(), value);
                  }
              };
          }
      
          private static InnerKey newInnerKey(GeoData geoDataKey) {
              return new InnerKey(geoDataKey.country, geoDataKey.region);
          }
      
          private static GeoData newGeoDataKey(InnerKey innerKey) {
              return new GeoData(innerKey.country, innerKey.region, null);
          }
      
      }
      

      【讨论】:

        【解决方案3】:

        创建一个包装器/装饰器类,它接受构造函数参数中的关键对象并按照您需要的方式计算其hashCodeequals。然后使用这个包装类作为键,而不是原来的键。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-02-09
          • 1970-01-01
          • 1970-01-01
          • 2014-05-27
          • 2014-03-19
          • 1970-01-01
          • 1970-01-01
          • 2020-01-30
          相关资源
          最近更新 更多