2009-04-22 6 views
3

Voilà Pair.javaJava génériques Paire <String, String> stockées dans la valeur HashMap pas récupérer key-> correctement

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

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

    } 
} 

Voilà mon problème, la dernière HM1 L'appel .containsKey devrait (j'attends naïvement) retourner la valeur stockée où la paire < "trois", "trois"> est vraie - je devrais obtenir une chaîne avec une valeur de "SEPT". Voici le résultat:

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

est donc à la fois p7.hashCode() et pSrch.hashCode() sont égaux et p7.equals (pSrch) et pSrch.equals (p7) et hm1.containsValue (p7) == true, je m'attendrais à ce que hm1.containsValue (pSrch) renvoie aussi true, mais ce n'est pas le cas. Qu'est-ce que je rate?

Répondre

24

Vous devez remplacer la méthode par la méthode equals de la classe java.lang.Object.

Au lieu de cela, vous avez surchargé la méthode avec une version supplémentaire qui prend un Pair. Méthode totalement différente qui ne s'appelle jamais. Remplacez votre equals avec quelque chose comme ceci:

@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); 
} 

Pour éviter ce genre d'erreur, utilisez l'annotation @Override sur les méthodes que vous comptez agir comme overrides. Vous obtiendrez une erreur de compilation quand ils ne le font pas.

+0

Key_ et Value_ pourraient être null. (Les noms incorrects, btw questionneur original.) –

+0

Je suis l'affiche originale, je ne suis pas sûr que je suis sur le nom incorrect des bits? Que dans equals() que p1.Key_ pourrait être nul? Oui correct. Merci pour ça. –

+1

Vous voudrez probablement tester si this.getClass() == o.getClass(), au lieu de simplement instanceof, sauf si vous voulez vraiment qu'une sous-classe de Pair soit (éventuellement) égale à une instance de Pair. Cela pourrait conduire à ce que votre implémentation de equals() ne soit pas réflexive - ce qui signifie que c'est possible pour a.equals (b)! = B.equals (a).Cela violerait le contrat déclaré de Object.equals(). –

5

Vous auriez dû remarquer qu'il n'imprime pas "in equals()" après "starting containsKey search2". Vous pouvez également déboguer dans HashMap pour voir que la méthode .equals() est appelée et renvoie false. En effet,

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

ne l'emportait pas sur

public boolean equals(Object obj) 

défini dans java.lang.Object

Changer votre code à

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

et il fonctionne. Vous pouvez éviter de tels bugs à l'avenir en mettant @Override annotation avant la méthode que vous pensez que vous êtes en train de remplacer. Si vous ne le remplacez pas, le compilateur vous le dira. Ce

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

provoque une erreur de compilation. Ce

@Override public boolean equals(Object obj) 

n'en a pas. Un bon IDE (Intellij IDEA par exemple) montre aussi quelles méthodes sont remplacées.

+0

Je savais que c'était quelque chose de simple - mais ces appels à hashCode() provenaient de la HashMap elle-même - je considérais que c'était un mappage rapide via hashCode. –

Questions connexes