【问题标题】:Java generics Pair<String, String> stored in HashMap not retrieving key->value properly存储在 HashMap 中的 Java 泛型 Pair<String, String> 无法正确检索 key->value
【发布时间】:2010-10-21 05:36:59
【问题描述】:

这里是 Pair.java

import java.lang.*; 
import java.util.*; 

public class Pair<TYPEA, TYPEB> implements Comparable< Pair<TYPEA, TYPEB> > {
  protected final TYPEA Key_;
  protected final TYPEB Value_;

  public Pair(TYPEA key, TYPEB value) {
    Key_   = key;
    Value_ = value;
  }
  public TYPEA getKey() {
    return Key_;
  }
  public TYPEB getValue() {
    return Value_;
  }
  public String toString() {
    System.out.println("in toString()");
    StringBuffer buff = new StringBuffer();
      buff.append("Key: ");
      buff.append(Key_);
      buff.append("\tValue: ");
      buff.append(Value_);
    return(buff.toString() );
  }
  public int compareTo( Pair<TYPEA, TYPEB> p1 ) { 
    System.out.println("in compareTo()");
    if ( null != p1 ) { 
      if ( p1.equals(this) ) { 
        return 0; 
      } else if ( p1.hashCode() > this.hashCode() ) { 
            return 1;
      } else if ( p1.hashCode() < this.hashCode() ) { 
        return -1;  
      }
    }
    return(-1);
  }
  public boolean equals( Pair<TYPEA, TYPEB> p1 ) { 
    System.out.println("in equals()");
    if ( null != p1 ) { 
      if ( p1.Key_.equals( this.Key_ ) && p1.Value_.equals( this.Value_ ) ) { 
        return(true);
      }
    }
    return(false);
  }
  public int hashCode() { 
    int hashCode = Key_.hashCode() + (31 * Value_.hashCode());
    System.out.println("in hashCode() [" + Integer.toString(hashCode) + "]");
    return(hashCode);
  }
}

这是测试用例:

import java.lang.*; 
import java.util.*;

import junit.framework.*;

public class PairTest extends TestCase { 

  public void testPair() { 
    String key   = new String("key"); 
    String value = new String("asdf"); 

    Pair<String, String> pair = new Pair<String, String>( key, value ); 

    assertTrue( pair.getKey().equals( key ) );
    assertTrue( pair.getValue().equals( value ) );
    assertTrue( pair.equals( new Pair<String, String>(key, value)) );
  }

  public void testPairCollection() { 

    HashMap< Pair<String, String>, String> hm1 = new HashMap<Pair<String,String>, String>(); 

    Pair<String, String> p1 = new Pair<String, String>("Test1", "Value1"); 
       hm1.put(p1, "ONE");  
    Pair<String, String> p2 = new Pair<String, String>("Test1", "Value2"); 
       hm1.put(p2, "TWO");  
    Pair<String, String> p3 = new Pair<String, String>("Test2", "Value1"); 
       hm1.put(p3, "THREE");    
    Pair<String, String> p4 = new Pair<String, String>("Test2", "Value2"); 
       hm1.put(p4, "FOUR"); 
    Pair<String, String> p5 = new Pair<String, String>("Test3", "Value1"); 
       hm1.put(p5, "FIVE"); 
    Pair<String, String> p6 = new Pair<String, String>("Test3", "Value2"); 
       hm1.put(p6, "SIX");  
    Pair<String, String> p7 = new Pair<String, String>("Test3", "Value3"); 
       hm1.put(p7, "SEVEN");    

    assertTrue( hm1.size() == 7 ); 

    Pair<String, String> pSrch = new Pair<String, String>("Test3", "Value3"); 
    assertTrue( p7.equals(pSrch) );
    assertTrue( pSrch.equals(p7) );
    assertTrue( p7.hashCode() == pSrch.hashCode() ); 
    assertTrue( 0 == p7.compareTo( pSrch ) );
    assertTrue( 0 == pSrch.compareTo(p7) );

    System.out.println("starting containsKey search");
    assertTrue( hm1.containsKey( p7 ) );
    System.out.println("starting containsKey search2");
    assertTrue( hm1.containsKey( pSrch ) );
    System.out.println("finishing containsKey search");

    String result = hm1.get( pSrch );
    assertTrue( null != result );
    assertTrue( 0 == result.compareTo("SEVEN"));

  } 
}

这是我的问题,最后一个 hm1.containsKey 调用应该(我天真地期望)返回存储在 Pair 为真的地方的值 - 我应该得到一个值为 "SEVEN" 的字符串。这是输出:

Running in equals()
in hashCode() [1976956095]
in hashCode() [1976956126]
in hashCode() [1976956096]
in hashCode() [1976956127]
in hashCode() [1976956097]
in hashCode() [1976956128]
in hashCode() [1976956159]
in equals()
in equals()
in hashCode() [1976956159]
in hashCode() [1976956159]
in compareTo()
in equals()
in compareTo()
in equals()
starting containsKey search
in hashCode() [1976956159]
starting containsKey search2
in hashCode() [1976956159]     <--- Bug here?

Never reaches 
          String result = hm1.get( pSrch );

p7.hashCode() 和 pSrch.hashCode() 都是相等的,p7.equals(pSrch) 和 pSrch.equals(p7) 和 hm1.containsValue(p7) == true,我希望 hm1。 containsValue(pSrch) 也会返回 true,但事实并非如此。我错过了什么?

【问题讨论】:

    标签: java string generics hashmap


    【解决方案1】:

    你应该注意到它在“starting containsKey search2”之后没有打印“in equals()”。您也可以调试到 HashMap 以查看 .equals() 方法被调用并返回 false。那是因为

    public boolean equals( Pair<TYPEA, TYPEB> p1 )
    

    不会覆盖

    public boolean equals(Object obj)
    

    在 java.lang.Object 中定义

    把你的代码改成

      public boolean equals( Object obj ) {
        if (!(obj instanceof Pair)) return false;
        Pair p1 = (Pair) obj;
    

    它有效。您可以通过在您认为要覆盖的方法之前放置 @Override 注释来避免将来出现此类错误。如果您实际上没有覆盖它,编译器会告诉您。这个

    @Override public boolean equals( Pair<TYPEA, TYPEB> p1 )
    

    导致编译错误。这个

    @Override public boolean equals( Object obj )
    

    没有。好的 IDE(例如 Intellij IDEA)也显示了哪些方法被覆盖了。

    【讨论】:

    • 我知道这很简单——但那些对 hashCode() 的调用来自 HashMap 本身——我认为它是通过 hashCode 进行快速映射的。
    【解决方案2】:

    您需要覆盖来自java.lang.Object 类的equals 方法。

    相反,您重载了该方法,添加了一个采用Pair 的附加版本。永远不会被调用的完全不同的方法。将您的 equals 替换为以下内容:

    @Override
    public boolean equals(Object o) { 
      System.out.println("in equals()");
      if (o instanceof Pair) { 
        Pair<?, ?> p1 = (Pair<?, ?>) o;
        if ( p1.Key_.equals( this.Key_ ) && p1.Value_.equals( this.Value_ ) ) { 
          return(true);
        }
      }
      return(false);
    }
    

    为避免此类错误,请在您打算用作覆盖的方法上使用 @Override 注释。如果没有,你会得到一个编译时错误。

    【讨论】:

    • Key_ 和 Value_ 可以为空。 (名字不正确,顺便说一句,原来的提问者。)
    • 我是原始发帖人,我不确定我是否关注了不正确的名称?在 equals() 中 p1.Key_ 可能为空?是,对的。谢谢你。
    • 你可能想要测试 this.getClass() == o.getClass(),而不仅仅是 instanceof,除非你真的希望 Pair 的子类(可能)等于一对。这可能会导致您的 equals() 实现不具有自反性——这意味着 a.equals(b) != b.equals(a) 是可能的。这将违反 Object.equals() 的约定。
    • 或者创建类,或者至少是相关的方法,final。必须仔细设计类以实现可扩展性。这不是(未记录的方法的自我使用,未定义的对等式的改进等)。所以最安全的选择是让它最终化。组合优于继承。
    猜你喜欢
    • 2021-07-15
    • 2021-09-05
    • 2021-04-10
    • 2012-06-18
    • 1970-01-01
    • 2021-09-13
    • 2013-10-04
    • 1970-01-01
    相关资源
    最近更新 更多