2011-05-20 194 views
5

我正在尝试创建一个Hashmap来为我执行查找。但是,当我运行此测试代码时,输​​出为空。我认为这应该与钥匙的存储性质有关,但我并不积极。也许这是一个类似的怪癖,就像var1 == var2不等于它们,除非它们指向内存中的相同对象,而是必须使用var1.equals(var2)为什么这个HashMap.get返回null?

有两个类来测试这个。

TestCard.java

import java.util.HashMap; 

public class TestCard { 

    // HashMap for SpecialK Lookup 
    private static HashMap<Card, Integer> specialKLookup = new HashMap<Card, Integer>(); 

    // Constructor 
    public TestCard(){ 
    } 

    public static void main(String[] args) { 
     Card[] cards = new Card[3]; 
     cards[0] = new Card((short)12, (short)0); 
     cards[1] = new Card((short)0, (short)1); 
     cards[2] = new Card((short)5, (short)2); 

     /* Build SpecialK Lookup HashMap. 
     * Ace of Spades = 0 
     * Ace of Hearts = 1 
     * Ace of Diamonds = 2 
     * Ace of Clubs = 3 
     * ... 
     * Two of Clubs = 51 
     */ 
     Integer specialKCounter = 0; 
     for(int i=12;i>=0;i--){ 
       for (int j=0;j<4;j++){ 
         specialKLookup.put(new Card((short)i, (short)j), specialKCounter++); 
       } 
     } 

     System.out.println(specialKLookup.get(cards[0])); 
    } 
} 

Card.java

public class Card{ 
    private short rank, suit; 

    private static String[] ranks = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King", "Ace"}; 
    private static String[] suits = {"Spades", "Hearts", "Diamonds", "Clubs"}; 

    //Constructor 
    public Card(short rank, short suit){ 
     this.rank = rank; 
     this.suit = suit; 
    } 

    // Getter and Setters 
    public short getSuit(){ 
     return suit; 
    } 

    public short getRank(){ 
     return rank; 
    } 

    protected void setSuit(short suit){ 
     this.suit = suit; 
    } 

    protected void setRank(short rank){ 
     this.rank = rank; 
    } 
} 

回答

12

类(Card)丢失的equals(Object)hashCode()

一个正确的实施既没有将它定义将只是不工作这些。 (它编译得很好,因为这些方法都是虚拟的,并且在所有对象中都是继承的,因为它们是Object的一部分:HashMap无法在编译时强制执行此操作。)请参阅上面有关所需合约的链接。

这些方法都需要被执行,因为hashCode确定在HashMap中实现中使用的散列的桶和equals是保证一个目的是数值等于(多个对象可以具有相同hashCode,这就是为什么equals也是必需的)。请参阅Hash table以获取更一般的哈希详细信息。

如果这些方法没有被重载,那么使用在Object中定义的实现。也就是说,x.equals(y)已经接近-x == y语义和hashCode返回每个合同的稳定数量。这有效地使得地图工作像标识图(当卡对象是密钥时):只有完全相同的对象可以检索先前存储的值 - 每隔一个get将返回null,如观察到的。

快乐编码。

1

您需要实现hashCode和equals方法,因为这允许在两个不同的对象平等的测试,并且还有助于散列图存储。如果不实现这些,即使两个对象的属性相同,它们也会被视为不同的对象。有关更多详情,请参阅http://www.jchq.net/certkey/0902certkey.htm

2

也许这是一个类似的快速怎么样的VAR1 == VAR2不等于除非它们指向内存中的>同一个对象,相反,你必须使用var1.equals(VAR2)

几乎。正如您所期望的那样,哈希映射需要获取对象的哈希代码的方法。在Java中,这是由Object实现的hashCode method提供的,但需要由您的Card类覆盖。

*更新:正如pst指出的那样,它也必须重新实现等于。

+1

它还必须按合同执行“equals”。 – 2011-05-20 22:36:23

3

确实是因为这个问题。

您需要定义卡上的平等含义,因此您需要覆盖equalshashCode方法。

如果你不这样做,它假设两张卡片只有相同的实例才相等。 (如在equals默认行为。)

需要注意的是,这是你会覆盖equalshashCode,作为两个对象that're等于必须散列为相同的值的HashMap正常工作非常重要。请参阅Overriding equals and hashCode in Java

1

对于Card,您必须覆盖hashCode()方法,并且当且仅当卡片相等时才返回相同的值 - 您也应该覆盖equals()。因为这就是HashMap为了找到键引用的对象所依赖的东西;现在,它是从Object继承的那些正在使用的方法的版本,只有当您使用相同的对象作为键时才会匹配,而您正在创建新的,尽管“相等”的。

+1

如果'equals()'返回true,则'hashCode()'必须相同。如果equals()返回false,hashCode()的结果仍然可能相等,但也可能不同。如果'hashCode()'的结果不同,'equals()'必须返回false。 – Arjan 2011-05-20 22:41:25

+0

这个答案不正确,因为两个不等于对象可以具有相同的哈希码(即,“当且仅当卡相等”的说法不正确)。 Arjan正确地总结它。 – 2011-05-21 02:25:26

+0

@SteveKuo所以如果我删除了'只有'从我写的东西',答案将不再是不正确的,但你的意见将停止合理。我该怎么办? – entonio 2013-02-18 21:58:49