13

自动装箱相当可怕。虽然我完全理解 和 之间的区别,==.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)return true 你已经破坏了代码。

这不能通过在字节码中用 int 替换 Integer 来在编译器级别解决(只要它从未被分配为 null)

4

6 回答 6

12
  • 他们为什么这样做?

-128 到 127 之间的每个整数都被 java 缓存。据推测,他们这样做是为了提高性能。即使他们现在想重新做出这个决定,他们也不太可能这样做。如果有人根据此构建代码,那么他们的代码在取出时就会中断。对于爱好编码,这可能无关紧要,但对于企业代码,人们会感到不安并且会发生诉讼。

  • 他们为什么不缓存程序使用的所有整数?

不能缓存所有整数,因为内存影响将是巨大的。

  • 为什么 JVM 不总是自动拆箱到原始?

因为 JVM 无法知道你想要什么。此外,此更改很容易破坏不是为处理这种情况而构建的遗留代码。

如果 JVM 在调用 == 时自动拆箱到原语,这个问题实际上会变得更加混乱。现在你需要记住 == 总是比较对象引用,除非对象可以被拆箱。就像您上面所说的那样,这将导致更奇怪的令人困惑的情况。

与其过分担心这一点,不如记住这条规则:

切勿将对象与 == 进行比较,除非您打算通过它们的引用来比较它们。如果你这样做,我想不出你会遇到问题的场景。

于 2010-04-08T19:20:09.760 回答
7

你能想象如果每个人都Integer承担了拘留的开销,会有多糟糕的表现吗?也不适用于new Integer.

Java 语言(不是 JVM 问题)不能总是自动拆箱,因为为 1.5 之前的 Java 设计的代码仍然可以工作。

于 2010-04-08T19:04:18.930 回答
5

Integer字节范围内的 s 是同一个对象,因为它们被缓存了。Integer字节范围之外的 s 不是。如果要缓存所有整数,想象一下所需的内存。

这里开始

所有这些魔法的结果是,您可以在很大程度上忽略 int 和 Integer 之间的区别,但有一些警告。整数表达式可以有一个空值。如果您的程序尝试自动拆箱 null,它将抛出 NullPointerException。== 运算符对 Integer 表达式执行引用标识比较,对 int 表达式执行值相等比较。最后,与装箱和拆箱相关的性能成本,即使它是自动完成的

于 2010-04-08T19:11:12.857 回答
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 默认将自动装箱作为警告的原因。

于 2010-04-08T19:28:59.043 回答
3

很多人都遇到过这个问题,甚至是写 Java 书籍的人。

Pro Java Programming中,作者讨论了在 IdentityHashMap 中使用自动装箱整数作为键的问题,他在 WeakHashMap 中使用自动装箱整数键。他使用的示例值大于 128,因此他的垃圾回收调用成功。如果有人要使用他的示例并使用小于 128 的值,那么他的示例将失败(由于密钥被永久缓存)。

于 2010-04-08T23:15:58.577 回答
2

当你写

foo.get(0)

编译器与您如何创建列表无关。它只查看 List foo 的编译时类型。因此,如果这是一个 List<Integer>,它会将其视为 List<Integer>,正如它应该做的那样,并且 List<Integer> 的 get() 总是返回一个 Integer。如果你想使用 == 那么你必须写

System.out.println(foo.get(0).intValue() == bar.get(0).intValue());

不是

System.out.println(foo.get(0) == bar.get(0));

因为那有完全不同的含义。

于 2011-06-01T08:26:58.827 回答