Autoboxing相当可怕。虽然我完全理解==
和.equals
之间的区别,但我不得不帮助我解决跟踪错误:
final List<Integer> foo = Arrays.asList(1, 1000);
final List<Integer> bar = Arrays.asList(1, 1000);
System.out.println(foo.get(0) == bar.get(0));
System.out.println(foo.get(1) == bar.get(1));
打印
true
false
他们为什么这样做?它与缓存的整数有关,但如果是这样的话,为什么它们不只是缓存程序使用的所有整数?或者为什么JVM始终不会自动拆箱到原语?
打印false false或true true会更好。
修改
我不同意旧代码的破坏。通过让foo.get(0) == bar.get(0)
返回true,您已经破坏了代码。
这不能在编译器级别通过在字节代码中用int替换Integer来解决(只要它永远不会被指定为null)
答案 0 :(得分:11)
-128和127之间的每个整数都由java缓存。据推测,他们这样做是为了获得性能优势。即使他们现在想要回到这个决定,他们也不太可能。如果有人根据这个构建代码,它们的代码会在取出时中断。对于业余爱好编码,这可能并不重要,但对于企业代码,人们会感到不安并且会发生诉讼。
所有整数都不能被缓存,因为内存的影响是巨大的。
因为JVM无法知道你想要什么。此外,此更改可能很容易破坏未构建用于处理此案例的遗留代码。
如果JVM在调用==时自动取消装箱到原语,这个问题实际上会变得更加混乱。现在您需要记住==始终比较对象引用,除非可以取消装箱对象。这会引起更多奇怪的混淆案例,就像你上面提到的那样。
而不是过于担心这一点,只需记住这条规则:
从不将对象与==进行比较,除非您打算通过引用对它们进行比较。如果你这样做,我想不出你遇到问题的情况。
答案 1 :(得分:7)
你能想象如果每个Integer
带来了对拘禁的开销会有多糟糕的表现吗?也不适用于new Integer
。
Java语言(不是JVM问题)无法始终自动取消装箱,因为为1.5之前的Java设计的代码仍然有用。
答案 2 :(得分:5)
Integer
是同一个对象,因为它们是缓存的。字节范围之外的Integer
不是。如果要缓存所有整数,请想象所需的内存。
来自here
所有这些魔力的结果是你可以在很大程度上忽略int和Integer之间的区别,但需要注意几点。 Integer表达式可以具有空值。如果您的程序尝试autounbox null,它将抛出NullPointerException。 ==运算符对整数表达式执行引用标识比较,并对int表达式执行值相等比较。最后,即使是自动完成,也存在与装箱和拆箱相关的性能成本
答案 3 :(得分:4)
如果您完全跳过自动装箱,您仍会遇到此行为。
final List<Integer> foo =
Arrays.asList(Integer.valueOf( 1 ), Integer.valueOf( 1000 ));
final List<Integer> bar =
Arrays.asList(Integer.valueOf( 1 ), Integer.valueOf( 1000 ));
System.out.println(foo.get(0) == bar.get(0)); // true
System.out.println(foo.get(1) == bar.get(1)); // false
如果您想要特定的行为,请更明确:
final List<Integer> foo =
Arrays.asList( new Integer( 1 ), new Integer( 1000 ));
final List<Integer> bar =
Arrays.asList( new Integer( 1 ), new Integer( 1000 ));
System.out.println(foo.get(0) == bar.get(0)); // false
System.out.println(foo.get(1) == bar.get(1)); // false
这就是为什么Eclipse默认将自动装箱作为警告的原因。
答案 4 :(得分:3)
很多人都遇到过这个问题,甚至是那些写有关Java的书籍的人。
在Pro Java Programming中,作者在谈到使用自动装箱整数作为IdentityHashMap中的键的问题时,仅使用了几英寸,他在WeakHashMap中使用自动装箱的整数键。他使用的示例值大于128,因此他的垃圾收集调用成功。如果有人使用他的示例并使用小于128的值,他的示例将失败(由于密钥是永久缓存的)。
答案 5 :(得分:2)
写作时
foo.get(0)
编译器与您创建List的方式无关。它只查看List foo的编译时类型。因此,如果它是List&lt; Integer&gt;,它将把它视为List&lt; Integer&gt;,就像它应该做的那样,并且List&lt; Integer&gt;的get()总是返回一个Integer。如果你想使用==那么你必须写
System.out.println(foo.get(0).intValue() == bar.get(0).intValue());
不
System.out.println(foo.get(0) == bar.get(0));
因为它有完全不同的含义。