【问题标题】:Using two (or more) objects as a HashMap key使用两个(或更多)对象作为 HashMap 键
【发布时间】:2010-11-14 11:20:58
【问题描述】:

我想将某些对象存储在 HashMap 中。问题是,通常您只使用单个对象作为键。 (例如,您可以使用字符串。)我想做的是使用多个对象。例如,一个类和一个字符串。有没有一种简单而干净的方法来实现它?

【问题讨论】:

    标签: java hash map


    【解决方案1】:

    您的键必须实现 hashCode 和 equals。如果是SortedMap,还必须实现Comparable接口

    public class MyKey implements Comparable<MyKey>
    {
    private Integer i;
    private String s;
    public MyKey(Integer i,String s)
    {
    this.i=i;
    this.s=s;
    }
    
    public Integer getI() { return i;}
    public String getS() { return s;}
    
    @Override
    public int hashcode()
    {
    return i.hashcode()+31*s.hashcode();
    }
    
    @Override
    public boolean equals(Object o)
    {
    if(o==this) return true;
    if(o==null || !(o instanceof MyKey)) return false;
    MyKey cp= MyKey.class.cast(o);
    return i.equals(cp.i) && s.equals(cp.s);
        }
    
       public int compareTo(MyKey cp)
         {
         if(cp==this) return 0;
         int i= i.compareTo(cp.i);
         if(i!=0) return i;
         return s.compareTo(cp.s);
         }
    
    
     @Override
        public String toString()
           {
           return "("+i+";"+s+")";
           }
    
        }
    
    public Map<MyKey,String> map= new HashMap<MyKey,String>();
    map.put(new MyKey(1,"Hello"),"world");
    

    【讨论】:

      【解决方案2】:

      我倾向于使用列表

      map.put(Arrays.asList(keyClass, keyString), value)
      

      【讨论】:

      • 不,任何List(或至少任何AbstractListArrays.asList 是)的哈希码由其元素的哈希码决定。我只是查了一下,因为我有同样的想法。
      • 很容易做到,但它的缺点是没有记录列表中的内容。是类,字符串吗?还是先字符串?还是类名和字符串?
      • 是的,但是您可以通过一些方法来处理它,例如创建一个静态 getHashList 方法来为您完成此操作。另外,我当然会推荐 Pierre 的 Key Class 用于更长寿的地图。
      • 另一个问题是列表中 hashCode 和 equals 的实现与实现良好的自定义类相比代价高昂。
      • 贵怎么办?无论哪种方式,您都应该执行相同的操作。有一个额外的对象取消引用,但这不太可能导致缓存未命中。
      【解决方案3】:

      我知道的最简单的方法是创建一个包装类并覆盖 hashmap 和 equals。例如:

      public class KeyClass {
      
          private String element1;
          private String element2;
      
          //boilerplate code here
      
          @Override
          public boolean equals(Object obj) {
              if (obj instanceof KeyClass) {
                  return element1.equals(((KeyClass)obj).element1) &&
                      element2.equals(((KeyClass)obj).element2);
              }
              return false;
          }
      
          @Override
          public int hashCode() {
              return (element1 + element2).hashcode();
          }
      }
      

      当然,我会推荐使用 StringBuilder 和其他任何东西,但是这样您就覆盖了等号和哈希码,从而允许对您的多个键进行哈希和等式检查。

      另外,为了安全起见,我建议将对象设为不可变(不可编辑),但这纯粹是偏好。

      【讨论】:

        【解决方案4】:

        Apache Commons Collections 有一个多键映射,它可能会为您解决问题:

        https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/keyvalue/MultiKey.html

        看起来它最多可以处理 5 个“键”。

        【讨论】:

          【解决方案5】:

          您的意思是该对象将由两个键作为键,或者更确切地说是由两个东西组成的键。

          如果你想要第一种情况。也就是说,一个由两个键键控的对象,比如一个类或一个对象,你需要使用两个映射。

          Map<Key1, value>
          
          Map<Key2, value>
          

          在第二种情况下,您需要一张地图,所以:

          Map<Key1, Map<Key2, value>>
          

          【讨论】:

            【解决方案6】:

            您可以创建一个包含您想要作为键的类和字符串的持有者类。

            public class Key {
            
                public MyClass key_class;
                public String key_string;
            
                public Key(){
                    key_class = new MyClass();
                    key_string = "";
                }
            
            }
            

            可能不是最好的解决方案,但有可能。

            【讨论】:

            • 任何用作键的类都需要正确覆盖equals()和hashCode()。
            • 我喜欢这种方法,但是对于用例来说,添加 hashcode 和 equals 方法是强制性的。我也会让它成为不可变的。
            【解决方案7】:

            在某些地方,人们建议创建一个包含其他类的“Key”类,我完全同意。只是想我会添加一个有用的提示。

            如果您使用 eclipse 或 netbeans,它们有一个不错的选择——您可以告诉 Eclipse 创建基于一个或多个成员的 equals 和 hashcode 方法。因此,您只需选择要检索的成员(或多个成员),NB 就会创建您需要为您编写的大部分代码。

            当然,当我只想通过一个对象检索时,我通常只是将 hashcode 和 equals 方法委托给该对象(委托 equals 可能有问题,因为这意味着您的“密钥持有者”类之一将等于它是关键的对象,但这很容易修复(而且通常不会影响任何事情)

            所以我的头顶:

            class KeyHolder {
                public final String key;
                public final Object storeMe;
            
                public KeyHolder(String key, Object storeMe) {
                    this.key=key;
                    this.storeMe=storeMe;
                }
            
                public equals(Object o) {
                    return (o instanceof KeyHolder && ((KeyHolder)o).key.equals(key));
                }
            
                public hashcode() {
                    return key.hashCode();
                }
            }
            

            这就是它的全部内容,如果您要求,eclipse 会为您完成最后两个。

            顺便说一句,我知道我有公共成员,公共 final 成员与拥有 getter 完全一样——这并不是一个糟糕的主意。我最近开始在像这样的小型实用程序类上使用这种模式。如果成员不是最终成员,情况会更糟,因为这就像拥有一个二传手(这些天我试图避免的事情)。

            【讨论】:

              【解决方案8】:

              可以使用 apache 的 commons 集合库的 MultiKey class 来解决这个问题。 这是一个简单的例子:

              import org.apache.commons.collections.keyvalue.MultiKey;
              
              HashMap map = new HashMap();
              MultiKey multiKey = new MultiKey(key1, key2);
              
              map.put(multikey,value);
              
              //to get 
              map.get(new MultiKey(key1,key2)); 
              

              【讨论】:

              • 请注意,这与 Clay's answer 重复,尽管您提供了一个示例。
              猜你喜欢
              • 2013-12-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-07-06
              • 2015-11-23
              • 2017-09-03
              • 1970-01-01
              相关资源
              最近更新 更多