在Java中,您可以覆盖HashMap的密钥对象的equals和hash方法,以确定如何生成哈希代码以及何时应将两个关键对象视为相等。
是否有任何Map实现允许通过覆盖其哈希方法(并通过可被覆盖的equals(key1,key2)方法确定键相等性)来定义Map类中的哈希值和等号值?
使用案例
我们假设我们有一个GeoData类的对象,包含字段:country,region,city。我们想要访问两个地图:地图 x 存储每个地区的居民数量并映射 y 数量每个城市的居民。
要获取GeoData对象的两个信息,我们首先需要从该对象中提取国家和地区,然后创建一个新的类X,它定义哈希和等于考虑国家和地区,并将其用作地图的键 X 即可。此外,我们需要对国家,地区和城市做同样的事情。创建一个类Y的新对象,并使用它从地图 y 中获取值。
如果我们为每个地图 x 和 y 并覆盖哈希(GeoData键)和等于(GeoData key1,GeoData key2)方法,这也可以避免为每次访问创建新的密钥对象?
然后,哈希(GeoData键)可以使用地图 x 中的国家和地区,或地图 y中的国家/地区,城市 用于哈希码计算。
更新
正确标记为重复。 This回答暗示apache commons-collections AbstractHashMap就是我想要的。
答案 0 :(得分:3)
如果我理解正确,您希望在不同import UIKit
class CustomAccessoryView: UIView {
@IBAction func clickLoveButton(_ sender: UIButton) {
print("Love button clicked")
}
}
s中使用具有不同相等标准的相同密钥类型(GeoData
)。
如果您使用Map
代替HashMap
并将TreeMap
传递给每个Comparator<GeoData>
构造函数,则可以执行此操作(一个将比较两个TreeMap
比较国家,地区和城市的实例,另一个只比较国家和地区)。
答案 1 :(得分:2)
您还可以为地图创建装饰器。它不会避免为键创建新类,但它隐藏了它,因此映射的类型为Map<GeoData, Integer>
。当然,如果您执行keySet()
之类的操作,则city必须始终为null,因为此信息会丢失(它不是密钥的一部分)。
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);
}
}
答案 2 :(得分:1)
创建一个包装器/装饰器类,它接受构造函数参数中的键对象,并以您需要的方式计算其hashCode
和equals
。然后使用此包装类作为键而不是原始键。