2016-04-02 27 views
0

我有两个HashMap。 mapA.keySet()mapB.keySet()的子集。我想打印每个关键字mapA.get(key) != mapB.get(key)。但发生在下面的代码中一些奇怪的行为:如果语句后Java HashMap奇怪的行为值发生变化

private static void printMissingNums(int[] a, int[] b) { 
    Map<Integer, Integer> mapA = intArrToMap(a); 
    Map<Integer, Integer> mapB = intArrToMap(b); 

    Set<Integer> missingNums = new TreeSet<Integer>(); 
    for (int key : mapA.keySet()) { 
     //This version does not work! 
     if (mapA.get(key) != mapB.get(key)) { 
      missingNums.add(key); 
     } 

     /* This version works (if I comment out the if statement above 
      and remove the comments around this block of code) 
     int valA = mapA.get(key); 
     int valB = mapB.get(key); 
     if (valA != valB) { 
      missingNums.add(key); 
     } 
     */ 
    } 

    // Unrelated to the strange behavior 
    for (int key : mapB.keySet()) { 
     if (!mapA.containsKey(key)) { 
      missingNums.add(key); 
     } 
    } 

    for (int i : missingNums) { 
     System.out.print(i + " "); 
    } 
} 

我得到了奇怪的行为,当我使用的第一个if语句,想知道是怎么回事幕后/为什么它,因为我认为它应该不工作落后。对于我可以访问的特定输入,它会打印3个数字,分别为x,y,z。我检查了HashMaps,发现mapA.get(x) != mapB.get(x)mapA.get(y) != mapB.get(y),但mapA.get(z) == mapB.get(z)

我试着在if语句之前和之后打印值,这些值是相等的,但它以某种方式进入if语句。

注释掉的版本按我期望的那样工作。它只打印出x和y。发生什么事?为什么看起来像HashMap值正在改变,即使我没有改变任何东西?

这是输入:http://pastebin.com/JyYxspjx。第一行是第一个数组中的元素数量,后跟空格分隔的整数。 下一行之后是第二个数组中的元素数量,后跟空格分隔的整数。

为什么8622是唯一具有相同值的密钥,但比较结果是错误的?

+1

可能是因为评论版本比较了原语而其他版本的对象。 – dambros

+0

除了一个特定的键8622以外,怎么比较这些原语是很好的。(我将输入数组添加到原始帖子中)。 8622与其他比较有什么不同? – mkim123

回答

1

请不要使用==!=代替Object compare.use equals()

流动2整数是不同的对象:

Integer i1 = new Integer(1); 
Integer i2 = new Integer(1); 
System.out.println(i1==i2);//false 

变化的if语句:

if (mapA.get(key).equlas(mapB.get(key))) { 
+0

好吧有道理。那么使用for(int i:mapA.keySet())通常是一个糟糕的主意,因为映射是Integer对象到Integer对象?另外,如果我决定使用(int i:mapA.keySet()),实际发生了什么? – mkim123

+0

for(int i:mapA.keySet()):从Integer到int的自动unbox,当Integer为null时将导致NullPointerException。 – BlackJoker

1

在比较(==foo.equals(Obj obj))的差是基于所述对象的具体实现方式的equals()方法。运营商==表示same object,在缺省情况下,.equals()进行相同的比较。您必须重写equals方法才能更改此默认行为。

在Java Integer类将覆盖equals方法从same object改变行为等同原始值:

// From the java source 
public boolean equals(Object obj) { 
    if (obj instanceof Integer) { 
     return value == ((Integer)obj).intValue(); 
    } 
    return false; 
} 

在这里你可以看到Integer类实际上是比较类类型和平等然后原始值。在这种情况下,整数==整数比较是不同于比Integer.equals(整数)比较。

如果你很好奇,在Java Object类的equals实现是:

// From the Java source code 
public boolean equals(Object obj) { 
    return (this == obj); 
} 

因为一切最终扩展Java中的Object类您时刻“同一对象”为平等的,除非你的类或继承类重写equals方法。

在一个侧面节点上,如果你曾经覆盖&落实等于自己,一定也要重写&实现哈希码,以及(最IDE的将有自动代码生成,这和警告,如果你做一个&不是其他)。当重写equals时不覆盖hashcode意味着你的HashMap/HashSet集合很可能在map/set中找不到“equal”对象,因为它不一定会散列到同一个存储桶中。

另一种方法是使用Set操作方法。如果你不关心修改底层的地图,你可以这样做:(记住这实际上将修改键集/基础地图)

Set<Integer> uniqKeysInA = mapA.keySet().removeAll(mapB.keyset()) 

如果你在乎修改/维持原有的,你'd需要mapA的密钥集的防御副本进行操作:

Set<Integer> uniqKeysInA = (new HashSet<Integer>(mapA.keySet())).removeAll(mapB.keySet())