在我正在研究的程序中,我遇到了一个数据存储问题,特别是与ArrayLists有关。这不是我测试的实际代码,但它提供了我的意思的一个例子。
public class test
{
public static void test()
{
ArrayList<Integer> bob = new ArrayList<Integer>();
bob.add(129);
bob.add(129);
System.out.println(bob.get(0) == 129 );
System.out.println(bob.get(1) == 129 );
System.out.println(bob.get(0) == bob.get(1) );
}
}
如果你运行它,你会得到,真实,真实和错误。代码识别两者都等于129,但由于某种原因,当它试图查看它们是否彼此相等时返回false。但是,如果将值更改为127,则返回true,true和true。对不同的值进行多次测试,您将看到接收true,true和true的最小值是-128,最大值是127.这是byte的间隔,这使我怀疑==操作使用在这种情况下是字节。
有趣的是,如果修改代码使其读取
public class test
{
public static void test()
{
ArrayList<Integer> bob = new ArrayList<Integer>();
bob.add(129);
bob.add(129);
int a = bob.get(0);
int b = bob.get(1);
System.out.println(a == 129 );
System.out.println(b == 129 );
System.out.println(a == b );
}
}
它按预期工作。输出true,true和true。为什么在比较之前将值保存为int会改变结果?是因为如果它们没有保存,比较将默认使用byte进行==比较吗?
答案 0 :(得分:3)
答案在于Java使用的原始包装类的缓存机制 在Integer的情况下,缓存-128到127之间的值(即一个字节的值范围)。
这意味着如果您在-128到127之间设置任何值,则可以从缓存中获取现成的实例。这就是==
运算符适用于那些运算符的原因,因为它比较了引用而不是值
另一方面,如果你使用任何其他值,你将获得每拳击一个全新的实例,这意味着==
运算符将失败。
以下是Integer
类中负责此事的代码:
private static class IntegerCache {
private IntegerCache(){}
static final Integer cache[] = new Integer[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Integer(i - 128);
}
}
答案 1 :(得分:2)
是的,我刚刚意识到我可以解释这一点。不知道我之前在想什么。
JLS,第5.1.7节:
如果被装箱的值p是-128和127之间的int类型的整数文字(§3.10.1),或者布尔文字是真或假(§3.10.3),或者是'\之间的字符文字u0000'和'\ u007f'包含(§3.10.4),然后让a和b成为p的任意两次拳击转换的结果。 a == b。
总是如此
当129
超出此范围时,除非您使用JVM标志自行配置范围,否则您将在索引1和2中最终得到两个不同的Integer
对象。
对于第一位代码中的最后一次比较:当ArrayList#get()
返回ArrayList
参数化类型的对象时,最后一次比较是比较两个Integer
个对象,并且因为两个对象是不同的,结果将是false
。前两个比较导致Integer
对象被取消装箱,因为您将Integer
包装器与int
字面值进行比较,因此结果与您预期的一样。
第二部分代码按预期工作,因为您正在比较int
文字,这些比较更直观。
答案 2 :(得分:1)
那是因为第三个测试比较了两个对象,因为get()
调用的(Integer对象)返回没有被取消装箱。结果为true的值使用缓存的单例,因此使用相同的对象,但在该范围之外,通过自动装箱将新的和不同的对象放入列表中。
请注意,此行为可能因JVM到JVM以及版本而异;在一些JVM上,它甚至可以基于一些启发式动态 - 例如,系统可以可以想象查看可用内存并缓存16位值而不是8位。
答案 3 :(得分:1)
Autoboxing和拆箱工作正在进行,这很有效 -
bob.add(129); // Autoboxed to Integer
int a = bob.get(0); // primitive int a
int b = bob.get(1); // primitive int b
System.out.println(a == 129 ); // primitive to primitive
System.out.println(b == 129 ); // primitive to primitive
System.out.println(a == b ); // primitive to primitive
您也可以使用Integer.intValue(),
Integer a = bob.get(0);
Integer b = bob.get(1);
// Here you could omit one, but not both, calls to intValue()
System.out.println(a.intValue() == b.intValue()); // primitive to primitive
答案 4 :(得分:1)
这是因为前两个比较是在int值上,因为bob.get()
在比较之前被转换为int。在第三个,比较是在对象上,这就是你在-128 to 127
之外的值得到错误的原因,因为在这个范围内值是缓存的。
希望这有帮助。
答案 5 :(得分:1)
集合有2个get方法接受int和Integer与Integer集合,autoboxing正在做一些内部魔术来使用错误的方法(Effective Java)
根据需要使用显式装箱或取消装箱。