==比较在ArrayList比较中使用byte吗?

时间:2014-05-29 04:58:14

标签: java arraylist integer byte equals

在我正在研究的程序中,我遇到了一个数据存储问题,特别是与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进行==比较吗?

6 个答案:

答案 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)

根据需要使用显式装箱或取消装箱。