为什么不在Java中缓存整数?

Nul*_*ion 25 java autoboxing equality

我知道这个主题有类似的帖子,但它们并没有完全解决我的问题.当你这样做时:

Integer a = 10;
Integer b = 10;
System.out.println("a == b: " + (a == b));
Run Code Online (Sandbox Code Playgroud)

这将(显然)打印true大部分时间,因为[-128,127]范围内的整数以某种方式被缓存.但:

Integer a = new Integer(10);
Integer b = new Integer(10);
System.out.println("a == b: " + (a == b));
Run Code Online (Sandbox Code Playgroud)

会回来false.我理解我要求整数的新实例,但由于盒装原语在Java中是不可变的,并且机器已经在那里做"正确的事情"(如第一种情况所示),为什么会发生这种情况?

如果具有10的Integer的所有实例在内存中都是相同的对象,那会不会更有意义?换句话说,为什么我们没有"Integer interning",这类似于"String interning"?

更好的是,如果表示同一事物的盒装基元的实例(无论值(和类型)是否是同一个对象),它会更有意义吗?或者至少正确回答==

nec*_*cer 19

应该非常清楚的是,缓存具有不可接受的性能损失 - 每次创建Integer时都会进行额外的if语句和内存查找.仅此一点就掩盖了任何其他原因以及其他原因令人痛苦.

至于"正确"回答==,OP在他的正确性假设中是错误的.整数DO通过一般Java社区对正确性的期望来正确地响应==,当然还有规范对正确性的定义.也就是说,如果两个引用指向同一个对象,则它们是==.如果两个引用指向不同的对象,即使它们具有相同的内容也不会 ==.因此,new Integer(5) == new Integer(5)评估时应该不足为奇false.

更有趣的问题是为什么 new Object();每次都需要创建一个唯一的实例?即为什么new Object();不允许缓存?答案是wait(...)notify(...)电话.缓存new Object()s会不正确地导致线程彼此同步.

如果不是这样,那么Java实现可以new Object()用单例完全缓存s.

这应该解释为什么new Integer(5)必须完成7次才能创建7个唯一Integer对象,每个对象包含值5(因为Integer扩展Object).


次要的,不太重要的东西:这个不错的方案中的一个问题是自动装箱和自动装箱功能.如果没有这个功能,你就无法进行比较new Integer(5) == 5.为了实现这些,爪哇unboxes对象(和它没有框的原语).因此new Integer(5) == 5转换为:( new Integer(5).intValue() == 5不是 new Integer(5) == new Integer(5).

最后一两件事要明白的是,自动装箱的n被做new Integer(n).它是通过调用内部完成的Integer.valueOf(n).

如果您认为自己理解并希望自己测试,请预测以下程序的输出:

public class Foo {
  public static void main (String[] args) {
    System.out.println(Integer.valueOf(5000) == Integer.valueOf(5000));
    System.out.println(Integer.valueOf(5000) == new Integer(5000));
    System.out.println(Integer.valueOf(5000) == 5000);
    System.out.println(new Integer(5000) == Integer.valueOf(5000));
    System.out.println(new Integer(5000) == new Integer(5000));
    System.out.println(new Integer(5000) == 5000);
    System.out.println(5000 == Integer.valueOf(5000));
    System.out.println(5000 == new Integer(5000));
    System.out.println(5000 == 5000);
    System.out.println("=====");
    System.out.println(Integer.valueOf(5) == Integer.valueOf(5));
    System.out.println(Integer.valueOf(5) == new Integer(5));
    System.out.println(Integer.valueOf(5) == 5);
    System.out.println(new Integer(5) == Integer.valueOf(5));
    System.out.println(new Integer(5) == new Integer(5));
    System.out.println(new Integer(5) == 5);
    System.out.println(5 == Integer.valueOf(5));
    System.out.println(5 == new Integer(5));
    System.out.println(5 == 5);
    System.out.println("=====");
    test(5000, 5000);
    test(5, 5);
  }
  public static void test (Integer a, Integer b) {
    System.out.println(a == b);
  }
}
Run Code Online (Sandbox Code Playgroud)

对于额外的功劳,如果所有内容==都更改为,也会预测输出.equals(...)

更新:感谢用户@sactiw的评论:"默认的缓存范围是-128到127和java 1.6以后你可以通过传递-XX重置上限值> = 127:AutoBoxCacheMax = from command line"

  • @no_answer_not_upvoted:Java重载`==`用于基元的值比较和其他所有的参考比较,可能没用的设计是参考类型和禁用的基元之间的比较,但如果允许混合比较则会变得不确定[我个人认为] ==`应禁止*all*混合比较,而不仅仅涉及整数基元,或者特别涉及`double`和非`long`整数原语].给定`int i = 2; 整数I1 = new Integer(i); 整数I2 = new Integer(i);`,`==`现在实现了一个破坏的等价关系. (2认同)

JB *_*zet 7

这可能会破坏在此设计更改之前编写的代码,当时每个人都认为两个新创建的实例是不同的实例.它可以用于自动装箱,因为之前不存在自动装箱,但改变新装置的含义太危险了,可能不会带来太大的好处.短期对象的成本在Java中并不大,甚至可能低于维护长寿命对象缓存的成本.


Mar*_*gus 5

如果您检查来源,您会看到:

/**
 * Returns an Integer instance representing the specified int value. If a new
 * Integer instance is not required, this method should generally be used in
 * preference to the constructor Integer(int), as this method is likely to
 * yield significantly better space and time performance by caching frequently
 * requested values.
 * 
 * @Parameters: i an int value.
 * @Returns: an Integer instance representing i.
 * @Since: 1.5
 */
 public static Integer valueOf(int i) {
      final int offset = 128;
      if (i >= -128 && i <= 127) { // must cache
          return IntegerCache.cache[i + offset];
      }
      return new Integer(i);
 }
Run Code Online (Sandbox Code Playgroud)

来源:链接

这就是为什么==用整数返回布尔值 true的性能原因——这完全是一个黑客。如果你想比较值,那么你有comparetoequals方法。

在其他语言中,比如也可以==用来比较字符串,基本相同的道理,被称为java语言最大的硬伤之一。

int是一种原始类型,由语言预定义并由保留关键字命名。作为原语,它不包含类或任何与类相关的信息。Integer是一个不可变的原始类,它通过包私有的本地机制加载并转换为 Class - 这提供了自动装箱并在 JDK1.5 中引入。之前的 JDK1.5intIntegerwhere 2 非常不同的东西。