Java 将接口用作hashmap中的键

Java 将接口用作hashmap中的键,java,generics,hashmap,Java,Generics,Hashmap,我尝试在hashMap中使用一个接口作为键,以便为多种类型的键创建一个映射。下面的方法似乎有效 interface Foo { void function(); } static class Bar implements Foo { private int id; public Bar(int id) { this.id = id; } @Override public void

我尝试在
hashMap
中使用一个接口作为键,以便为多种类型的键创建一个映射。下面的方法似乎有效

interface Foo {
        void function();
}
static class Bar implements Foo {

      private int id;
    
      public Bar(int id) {
          this.id = id;
      }
    
      @Override
      public void function() {
          System.out.println("this is bar");
      }
    
      @Override
      public boolean equals(Object o) {
           if (this == o) return true;
           if (o == null || getClass() != o.getClass()) return false;
           Bar bar = (Bar) o;
           return id == bar.id;
       }
    
       @Override
       public int hashCode() {
          return Objects.hash(id);
       }
}
    
public static Map<Foo, Integer> map = new HashMap<>();
    
    
     
static class Baz implements Foo {
    String name;
    public Baz(String name) {
        this.name = name;
    }

    @Override
    public void function() {
       System.out.println("this is Baz");
    }
    @Override
    public boolean equals(Object o) {
       if (this == o) return true;
       if (o == null || getClass() != o.getClass()) return false;
       Baz baz = (Baz) o;
       return name.equals(baz.name);
    }

    @Override
    public int hashCode() {
       return Objects.hash(name);
    }
}

public static void main(String[] args) {
    Bar bar = new Bar(123);
    Baz baz = new Baz("some name");

    map.put(bar, 10);
    map.put(baz, 20);
  
    System.out.println(map.get(bar));
 }
接口Foo{
空函数();
}
静态类栏实现了Foo{
私有int-id;
公共酒吧(内部id){
this.id=id;
}
@凌驾
公共空间功能(){
System.out.println(“这是酒吧”);
}
@凌驾
公共布尔等于(对象o){
如果(this==o)返回true;
如果(o==null | | getClass()!=o.getClass())返回false;
Bar=(Bar)o;
返回id==bar.id;
}
@凌驾
公共int hashCode(){
返回Objects.hash(id);
}
}
publicstaticmap=newhashmap();
静态类Baz实现了Foo{
字符串名;
公共Baz(字符串名称){
this.name=名称;
}
@凌驾
公共空间功能(){
System.out.println(“这是Baz”);
}
@凌驾
公共布尔等于(对象o){
如果(this==o)返回true;
如果(o==null | | getClass()!=o.getClass())返回false;
Baz-Baz=(Baz)o;
返回name.equals(baz.name);
}
@凌驾
公共int hashCode(){
返回Objects.hash(name);
}
}
公共静态void main(字符串[]args){
棒材=新棒材(123);
Baz Baz=新Baz(“某个名称”);
地图放置(条形图,10);
地图放置(巴兹,20);
System.out.println(map.get(bar));
}
我不确定的是,是否有某个角落的案例会打破这张地图?

有没有一种情况是,将接口作为键会发生故障?我可以使用泛型更简单一些吗?

理论上,由于不同的
hashCode
实现,可能会出现更多的哈希冲突问题,从而导致性能下降,因此您需要小心,并使用实际数据对其进行测试。除此之外,它是一个有效的用例。

唯一稍微与众不同的是equals方法必须比较Bar和Baz对象。当一个映射只有一种类型的对象时,equals方法中的check
this.getClass()==that.getClass
永远不会返回false。不过,您已经正确地实现了这一点,因此您无需担心任何问题

您可能会得到比预期更多的哈希冲突。假设您有两个类,它们都有一个int-id字段,并使用
对象实现hashCode。hash(id)
-现在,具有相同id的不同类的对象具有相同的hash代码。如果该用例是预期的,您可以以每个类特有的方式扰动哈希,例如,通过将特定于类的常数混合到哈希中:

@Override
public int hashCode() {
   return Objects.hash(1, id);
}

...

@Override
public int hashCode() {
   return Objects.hash(2, name);
}

拥有一组异构的键听起来有点奇怪。如果你有
类Babble extensed Bar
如果有人把一个Babble和你的Bar一起扔进去,它们就不能相等,即使它们有相同的id。也许可以使用,@matt:我猜你的意思是关于equals/hashCode的实现和继承。说得好。我的目标并不是要扩展
,但另一方面,我不认为我能够强制所有用作键的类成为
最终的
“有没有一种情况是,将接口作为键会发生故障?”。当然,如果有人用错误的hashcode/equals实现您的接口。或者只是一个可变类。现在不是每个人都通过IDE生成
hashCode
?实现仍然是一个问题吗?@Jim如果
hashCode
实现适合于单个类型,那么您可以期望良好的散列分布和良好的
HashMap
性能。对于多种类型的散列分布,很难进行推理。无论如何,如果你不想让
HashMap
保存大量的键(>10000),并且你的
hashCode
方法还可以,那么它应该可以正常工作。
很难对多种类型的散列分布进行推理。
这非常有趣。请你详细说明一下好吗?啊,太好了!我现在明白了。但是像1/2这样的常数会起作用吗?我的意思是,每个类不应该是一些素数或随机数吗?
对象。hash
方法解决了这个问题。当前实现通过将第一个值乘以素数并添加第二个值来组合两个值。