为什么编译器/JVM 不能让自动装箱“正常工作”?
自动装箱相当可怕。虽然我完全理解 ==
和 .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)
Autoboxing is rather scary. While I fully understand the difference between ==
and .equals
I can't but help have the follow bug the hell out of me:
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));
That prints
true
false
Why did they do it this way? It something to do with cached Integers, but if that is the case why don't they just cache all Integers used by the program? Or why doesn't the JVM always auto unbox to primitive?
Printing false false or true true would have been way better.
EDIT
I disagree about breakage of old code. By having foo.get(0) == bar.get(0)
return true you already broke the code.
Can't this be solved at the compiler level by replacing Integer with int in byte code (as long as it is never assigned null)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
-128 到 127 之间的每个整数都由 java 缓存。据推测,他们这样做是为了提高性能。即使他们现在想反悔这个决定,他们也不太可能会这么做。如果有人根据此构建代码,那么当它被删除时,他们的代码就会崩溃。对于业余编码来说,这也许并不重要,但是对于企业代码来说,人们会感到不安,并且会发生诉讼。
所有整数都不能被缓存,因为内存影响将是巨大的。
因为JVM无法知道你想要什么。此外,此更改很容易破坏不是为处理这种情况而构建的遗留代码。
如果 JVM 在调用 == 时自动拆箱为原语,这个问题实际上会变得更加混乱。现在您需要记住 == 始终比较对象引用,除非可以拆箱对象。这会导致更多奇怪的令人困惑的情况,就像您上面所说的那样。
不要太担心这个问题,只需记住这条规则:
永远不要将对象与 == 进行比较,除非您打算通过引用来比较它们。如果您这样做,我无法想象您会遇到问题的情况。
Every Integer between -128 and 127 is cached by java. They did this, supposedly, for the performance benefit. Even if they wanted to go back on this decision now, it's unlikely that they would. If anyone built code depending on this, their code would break when it was taken out. For hobby coding, this perhaps doesn't matter, but for enterprise code, people get upset and lawsuits happen.
All Integers cannot be cached, because the memory implications would be enormous.
Because the JVM cannot know what you wanted. Also, this change could easily break legacy code not built to handle this case.
If the JVM to automatically unboxed to primitives on calls to ==, this issue will actually become MORE confusing. Now you need to remember that == always compares object references, unless the Objects can be unboxed. This would cause yet more weird confusing cases just like the one you stated above.
Rather then worry too hard about this, just remember this rule instead:
NEVER compare objects with == unless you intend to be comparing them by their references. If you do that, I can't think of a scenario in which you'd run into an issue.
您能想象如果每个
Integer
都承担滞留开销,性能会有多糟糕吗?也不适用于new Integer
。Java 语言(不是 JVM 问题)不能总是自动拆箱,因为为 1.5 之前的 Java 设计的代码应该仍然可以工作。
Can you imagine how bad performance would be if every
Integer
carried overhead for internment? Also does not work fornew Integer
.The Java language (not a JVM issue) cannot always auto unbox because code designed for pre-1.5 Java should still work.
字节范围内的整数是同一个对象,因为它们被缓存了。字节范围之外的整数则不然。如果要缓存所有整数,请想象一下所需的内存。
并来自此处
Integer
s in the byte range are the same object, because they are cached.Integer
s outside the byte range are not. If all integers were to be cached, imagine the memory required.And from here
如果您完全跳过自动装箱,您仍然会遇到这种行为。
如果您想要特定的行为,请更加明确:
这就是 Eclipse 默认将自动装箱作为警告的原因。
If you skip autoboxing completely, you still get this behaviour.
Be more explicit if you want a specific behavior:
This is a reason, why Eclipse has autoboxing as a warning by default.
很多人都对这个问题有疑问,甚至是写 Java 书籍的人。
在 Pro Java 编程,仅在几英寸以下,作者讨论了使用自动装箱整数作为 IdentityHashMap 中的键的问题,他在弱哈希映射。他使用的示例值大于 128,因此他的垃圾回收调用成功。如果有人使用他的示例并使用小于 128 的值,他的示例将会失败(由于密钥被永久缓存)。
A lot of people have problems with this issue, even people that write books about Java.
In Pro Java Programming, mere inches below were the author talks about issues with using auto-boxed Integers as a key in an IdentityHashMap, he uses auto-boxed Integer keys in a WeakHashMap. The example values he uses are greater than 128, so his garbage collection call succeeds. If someone were to use his example and use values smaller than 128 though, his example would fail (due to the key being perma-cached).
当您编写
编译器时,如何创建列表并不重要。它只查看 List foo 的编译时类型。因此,如果这是一个 List,它会将其视为 List,正如它应该做的那样,并且 List的 get() 始终返回一个 Integer。如果你想使用 == 那么你必须写
not
因为它有完全不同的含义。
When you write
the compiler does not matter how you created the List. It only looks at the compile-time type of the List foo. So, if that is a List<Integer>, it will treat that as a List<Integer>, as it is supposed to do, and a List<Integer>'s get() always returns an Integer. If you want to use the == then you have to write
not
because that has a totally different meaning.