存储在 HashMap 中的 Java 泛型 Pair<String, String> 未正确检索键->值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/779414/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 19:23:39  来源:igfitidea点击:

Java generics Pair<String, String> stored in HashMap not retrieving key->value properly

javastringgenericshashmap

提问by Chris K

Here's Pair.java

这是 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);
  }
}

Here's the testcase:

这是测试用例:

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"));

  } 
}

Here's my problem, the last hm1.containsKey call should (I naively expect) return the value stored where Pair<"Three", "Three"> is true - I should get a String with a value of "SEVEN". Here is the output:

这是我的问题,最后一个 hm1.containsKey 调用应该(我天真地期望)返回存储在 Pair<"Three", "Three"> 为真的值 - 我应该得到一个值为“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 );

So is both p7.hashCode() and pSrch.hashCode() are equal and p7.equals(pSrch) and pSrch.equals(p7), and hm1.containsValue(p7) == true, I would expect hm1.containsValue(pSrch) would also return true, but it does not. What am I missing?

所以 p7.hashCode() 和 pSrch.hashCode() 相等,p7.equals(pSrch) 和 pSrch.equals(p7),以及 hm1.containsValue(p7) == true,我希望 hm1.containsValue(pSrch ) 也会返回 true,但它不会。我错过了什么?

采纳答案by erickson

You need to overridethe equalsmethod from the java.lang.Objectclass.

您需要覆盖类中的equals方法java.lang.Object

Instead, you've overloadedthe method with an additional version that takes a Pair. Totally different method that never gets called. Replace your equalswith something like this:

相反,您使用一个附加版本重载了该方法,该版本采用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);
}

To avoid this kind of mistake, use the @Overrideannotation on methods you intend to act as overrides. You'll get a compile time error when they don't.

为了避免这种错误,@Override在你打算作为覆盖的方法上使用注解。当他们不这样做时,你会得到一个编译时错误。

回答by Pavel Feldman

You should have noticed that it does not print "in equals()" after "starting containsKey search2". Also you could debug into HashMap to see that .equals() method is called and returns false. That's because

您应该已经注意到,在“启动 containsKey search2”之后它不会打印“in equals()”。您也可以调试到 HashMap 中以查看 .equals() 方法被调用并返回 false。那是因为

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

does NOT override

不覆盖

public boolean equals(Object obj)

defined in java.lang.Object

在 java.lang.Object 中定义

Change your code to

将您的代码更改为

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

and it works. You can avoid such bugs in the future by putting @Override annotation before method that you think you are overriding. If you are not actually overriding it, compiler will tell you. This

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

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

causes compilation error. This

导致编译错误。这个

@Override public boolean equals( Object obj )

does not. Also good IDE (Intellij IDEA for example) shows which methods are overriden.

才不是。同样好的 IDE(例如 Intellij IDEA)显示了哪些方法被覆盖。