为什么人们仍然在Java中使用原始types?

从Java 5开始,我们对原始types进行了装箱/拆箱操作,以便int被封装为java.lang.Integer ,等等。

我最近看到很多新的Java项目(这绝对需要JRE至less是版本5,如果不是6),使用int而不是java.lang.Integer ,尽pipe使用后者更方便一些辅助方法转换为long值等。

为什么有些人仍然在Java中使用原始types? 有没有实实在在的好处?

在Joshua Bloch的Effective Java中 ,第5项:“避免创build不必要的对象”,他发表了下面的代码示例:

 public static void main(String[] args) { Long sum = 0L; // uses Long, not long for (long i = 0; i <= Integer.MAX_VALUE; i++) { sum += i; } System.out.println(sum); } 

运行需要43秒。 如果把这个原始的东西带到原始的6.8秒,那么这就是为什么我们要使用原始的东西。

缺乏本地值的平等也是一个问题( .equals()==相比相当冗长)

对于biziclop:

 class Biziclop { public static void main(String[] args) { System.out.println(new Integer(5) == new Integer(5)); System.out.println(new Integer(500) == new Integer(500)); System.out.println(Integer.valueOf(5) == Integer.valueOf(5)); System.out.println(Integer.valueOf(500) == Integer.valueOf(500)); } } 

结果是:

 false false true false 

编辑 为什么(3)返回true和(4)返回false

因为它们是两个不同的对象。 最接近零的256个整数[-128; 127]被JVMcaching,所以它们返回相同的对象。 但是,在这个范围之外,它们不被caching,所以创build了一个新的对象。 为了使事情更加复杂,JLS要求至less有 256个flyweights被caching。 JVM实现者可能会添加更多,如果他们的愿望,这意味着这可以运行在一个系统上,最近的1024caching,所有这些都返回true … #awkward

Autounboxing会导致难以发现NPE

 Integer in = null; ... ... int i = in; // NPE at runtime 

在大多数情况下,null的赋值比上面的要less得多。

原始types:

 int x = 1000; int y = 1000; 

现在评估:

 x == y 

这是true 真不奇怪 现在尝试盒装types:

 Integer x = 1000; Integer y = 1000; 

现在评估:

 x == y 

这是false 大概。 取决于运行时间。 这个理由够了吗?

盒装types的性能较差,需要更多的内存。

除了性能和内存问题之外,我还想提出另一个问题: List接口将被打破,而不是int
问题是重载的remove()方法( remove(int)remove(Object) )。 remove(Integer)总是会解决调用后者,所以你不能通过索引删除一个元素。

另一方面,尝试添加和删除int时有一个缺陷:

 final int i = 42; final List<Integer> list = new ArrayList<Integer>(); list.add(i); // add(Object) list.remove(i); // remove(int) - Ouch! 

你能想象得到吗?

  for (int i=0; i<10000; i++) { do something } 

循环与java.lang.Integer而不是? java.lang.Integer是不可变的,因此循环中的每个增量都会在堆上创build一个新的java对象,而不是只用一条JVM指令递增堆栈中的int。 表演将是恶魔般的。

我真的不同意使用java.lang.Integer比int更方便。 反之。 Autoboxing意味着你可以使用int来强制使用Integer,而java编译器负责插入代码为你创build新的Integer对象。 自动装箱是所有关于允许您使用一个整型预期的int,与编译器插入相关的对象结构。 它绝不会消除或减less首先对int的需求。 随着自动装箱,你得到两全其美。 当你需要一个基于堆的Java对象时,你会自动为你创build一个Integer,而当你只是进行算术和本地计算时,你将获得一个int的速度和效率。

原始types快得多:

 int i; i++; 

整数(所有的数字,也是一个string)是一个不可变的types:一旦创build它不能改变。 如果i是Integer,比i++会创build一个新的Integer对象 – 在内存和处理器方面要贵得多。

首先,习惯。 如果你已经用Java编写了八年,你会积累一定的惯性。 为什么要改变,如果没有令人信服的理由呢? 这不像使用盒装原语带来任何额外的好处。

另一个原因是断言null不是一个有效的选项。 将两个数字的总和或循环variables声明为Integer是没有意义的,也是误导性的。

还有它的性能方面,而性能差异在很多情况下并不重要(尽pipe当它是非常糟糕的),没有人喜欢编写可以以更快的方式编写的代码习惯于。

顺便说一下,Smalltalk只有对象(没有原语),但他们已经优化了他们的小整数(不是所有的32位,只有27或者这样)不分配任何堆空间,而只是使用一个特殊的位模式。 其他常见的对象(true,false,null)也有特殊的位模式。

所以,至less在64位的JVM上(有一个64位的指针名称空间),应该可以不包含Integer,Character,Byte,Short,Boolean,Float(和Long Long)通过明确的new ...() ),只有特殊的位模式,可以被正常的操作符相当有效地操纵。

我不敢相信没有人提到我认为是最重要的原因:“int”比“Integer”更容易input。 我认为人们低估了简洁语法的重要性。 性能并不是真正的避免它们的原因,因为大多数时候使用数字的时候都是在循环索引中,并且在任何非平凡的循环(无论是使用int还是Integer)中增加并比较这些成本。

另一个原因是你可以得到NPE,但是用盒装types非常容易避免(只要你总是将它们初始化为非空值,就可以避免这种情况)。

另一个原因是(new Long(1000))==(new Long(1000))是错误的,但这只是另一种说法,“.equals”没有对盒装types的语法支持(与运算符<,> ,=等),所以我们回到“简单语法”的原因。

我认为Steve Yegge的非原始循环示例非常好地说明了我的观点: http : //sites.google.com/site/steveyegge2/language-trickery-and-ejb

考虑一下:与Java相比,使用函数types的语言有多好(比如任何函数式语言,python,ruby,甚至C语言),你必须使用Runnable和Callable等接口来模拟它们,无名的class级。

除了其他人所说的之外,原始局部variables不是从堆中分配的,而是在堆栈中分配的。 但是对象是从堆中分配的,因此必须进行垃圾回收。

几个原因不能摆脱原始:

  • 向后兼容。

如果被淘汰,任何旧的程序都不会运行。

  • JVM重写。

整个JVM将不得不被重写以支持这个新事物。

  • 更大的内存占用

你需要存储值和参考,它使用更多的内存。 如果你有一个巨大的字节数组,使用byte比使用Byte小得多。

  • 空指针问题。

声明int i然后做int i东西不会导致问题,但声明Integer i ,然后做同样会导致NPE。

  • 平等问题。

考虑这个代码:

 Integer i1 = 5; Integer i2 = 5; i1 == i2; // Currently would be false. 

会是假的。 运营商将不得不超载,这将导致重大的东西重写。

对象包装比原始对象要慢很多。

对象比原始types重得多,所以原始types比包装类的实例更有效率。

原始types非常简单:例如int为32位,在内存中占用32位,可以直接操作。 Integer对象是一个完整的对象,它(像任何对象)必须存储在堆上,只能通过引用(指针)访问它。 它最有可能也占用超过32位(4字节)的内存。

也就是说,Java在原始types和非原始types之间的区别也是Java编程语言时代的一个标志。 较新的编程语言没有这个区别; 如果你使用简单的值或更复杂的对象,这种语言的编译器就足够聪明,可以自己弄清楚。

例如,在Scala中没有原始types; 有一个Int类的整数,Int是一个真实的对象(你可以方法等)。 当编译器编译你的代码时,它在后台使用原始的int,所以使用Int和使用Java中的原始int一样有效。

很难知道封面上正在进行什么样的优化。

对于本地使用,当编译器有足够的信息进行优化排除空值的可能性时, 我期望性能是相同或相似的

但是,基元数组显然与盒装基元的集合非常不同 。 这是有道理的,因为在一个集合内很less有优化是可能的。

而且,与int相比, Integer具有更高的逻辑开销 :现在您不必担心int a = b + c; 抛出exception。

我会尽可能地使用原语,并依靠工厂方法和自动装箱,在需要时给我更多的语义上强大的盒装types。

原始types有很多优点: – 更简单的代码写入 – 性能更好,因为你没有为variables实例化对象 – 因为它们不代表对对象的引用,所以不需要检查空值 – 除非你使用原始types需要利用拳击function。

 int loops = 100000000; long start = System.currentTimeMillis(); for (Long l = new Long(0); l<loops;l++) { //System.out.println("Long: "+l); } System.out.println("Milliseconds taken to loop '"+loops+"' times around Long: "+ (System.currentTimeMillis()- start)); start = System.currentTimeMillis(); for (long l = 0; l<loops;l++) { //System.out.println("long: "+l); } System.out.println("Milliseconds taken to loop '"+loops+"' times around long: "+ (System.currentTimeMillis()- start)); 

毫秒采取循环“100000000”左右长:468

毫秒循环长达100000000次:31

在一个侧面说明,我不介意看到这样的事情发现它的方式到Java。

 Integer loop1 = new Integer(0); for (loop1.lessThan(1000)) { ... } 

for循环自动将loop1从0增加到1000或

 Integer loop1 = new Integer(1000); for (loop1.greaterThan(0)) { ... } 

for循环自动将loop1 1000递减到0。

我同意以前的答案,使用原始包装对象可能是昂贵的。 但是,如果性能在您的应用程序中并不重要,则在使用对象时避免溢出。 例如:

 long bigNumber = Integer.MAX_VALUE + 2; 

bigNumber的值是-2147483647,你会期望它是2147483649.这是一个代码中的错误,可以通过执行来修复:

 long bigNumber = Integer.MAX_VALUE + 2l; // note that '2' is a long now. 

bigNumber将是2147483649.这些types的错误有时很容易被忽略,并可能导致未知的行为或漏洞(参见CWE-190 )。

如果使用包装对象,则等效代码将不会编译。

 Long bigNumber = Integer.MAX_VALUE + 2; // Not compiling 

所以通过使用基元包装器对象来阻止这类问题更容易。

你的问题已经如此回答了,我的回复只是添加了一些前面没有提到的信息。

1)你需要primefaces做math运算2)基元占用较less的内存,如上所述,performance更好

你应该问为什么类/对象types是必需的

有对象types的原因是为了让我们的生活更轻松,当我们处理集合。 基元不能直接添加到List / Map,而是需要编写一个包装类。 Readymade Integer类的类可以帮助你在这里加上它有许多实用的方法,如Integer.pareseInt(str)

因为JAVA执行原始types的所有math运算。 考虑这个例子:

 public static int sumEven(List<Integer> li) { int sum = 0; for (Integer i: li) if (i % 2 == 0) sum += i; return sum; } 

在这里,提醒和一元加操作不能应用于整数(引用)types,编译器执行拆箱操作。

所以,确保在java程序中有多less个自动装箱和拆箱操作。 因为,执行这个操作需要时间。

一般来说,最好保留Referencetypes的参数和原始types的结果。