183

Since Java 5, we've had boxing/unboxing of primitive types so that int is wrapped to be java.lang.Integer, and so and and so forth.

I see a lot of new Java projects lately (that definitely require a JRE of at least version 5, if not 6) that are using int rather than java.lang.Integer, though it's much more convenient to use the latter, as it has a few helper methods for converting to long values et al.

Why do some still use primitive types in Java? Is there any tangible benefit?

nbro
  • 15,395
  • 32
  • 113
  • 196
Naftuli Kay
  • 87,710
  • 93
  • 269
  • 411
  • 56
    ever thought about memory consumption and performance? – Tedil Mar 04 '11 at 21:19
  • 1
    @Tedil Ok, so why would they even introduce this feature, as it's essentially a "trap" according to you; perceived better features at the cost of memory and performance? I'm just saying. – Naftuli Kay Mar 04 '11 at 21:25
  • 1
    @TK Because putting primitives in collections is problematic. Basically they expect developers to be aware of the performance issues when they use features. – corsiKa Mar 04 '11 at 21:26
  • @TK: They introduced it because sometimes you have to, or want to, use the wrapper objects (any time you want to store the value in a collection or pass it to a method requiring an object) and it makes doing so much more convenient. – ColinD Mar 04 '11 at 21:28
  • 1
    @glowcoder/ColinD Very interesting. I guess my subsequent question is "Why in the $@#$ haven't they optimized the JRE to essentially negate this performance impact by simply making the wrapper classes invisible and weightless? I always thought of "autoboxing" as something that only really affected devs when __writing__ code as a benefit to ease of use. – Naftuli Kay Mar 04 '11 at 21:34
  • 9
    @TK Kocheran Mostly because `new IntegeR(5) == new Integer(5)` should by the rules, evaluate to false. – biziclop Mar 04 '11 at 21:39
  • 1
    @TK: Autoboxing is just syntactic sugar. Underneath, objects still have to be objects and primitives still have to be primitives. There are certain critical differences in how those function that, I'd guess, prevent them from making object wrappers something "invisible and weightless". – ColinD Mar 04 '11 at 21:45
  • 1
    Why don't they just allow to overload operators... This `.equals()` stuff really makes me think worse about Java. – Oleh Prypin Mar 05 '11 at 12:52
  • 10
    See GNU Trove or Mahout Collections or HPPC or ... for solutions to collections of primitive types. Those of us who care about speed spend our time using *more* primitive types, not less. – bmargulies Mar 05 '11 at 23:00
  • 1
    @corsiKa When you use the `new` operator it will *always* create a new object. You asked for a new object, and you will receive new object. – NullUserException May 09 '14 at 03:38
  • 3
    @OlehPrypin The good thing about not allowing operator overloading is that you can look at Java code and you will always know what it's doing. In a language like C++, you have no idea what the operators might have been overloaded to. – NullUserException May 09 '14 at 03:39
  • @NullUserException Yes, for sure. That's illustrated by my example, notably `class biziclip`, and in my hastily-made-before-the-five-minute-window-expires edit. – corsiKa May 09 '14 at 19:49
  • Unrelated, but this inconsistency in Java's type system is one of the best improvements Scala makes. – Paul Draper Jun 01 '14 at 02:05

21 Answers21

427

In Joshua Bloch's Effective Java, Item 5: "Avoid creating unnecessary objects", he posts the following code example:

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);
}

and it takes 43 seconds to run. Taking the Long into the primitive brings it down to 6.8 seconds... If that's any indication why we use primitives.

The lack of native value equality is also a concern (.equals() is fairly verbose compared to ==)

for 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));
    }
}

Results in:

false
false
true
false

EDIT Why does (3) return true and (4) return false?

Because they are two different objects. The 256 integers closest to zero [-128; 127] are cached by the JVM, so they return the same object for those. Beyond that range, though, they aren't cached, so a new object is created. To make things more complicated, the JLS demands that at least 256 flyweights be cached. JVM implementers may add more if they desire, meaning this could run on a system where the nearest 1024 are cached and all of them return true... #awkward

MC Emperor
  • 22,334
  • 15
  • 80
  • 130
corsiKa
  • 81,495
  • 25
  • 153
  • 204
  • 58
    Now imagine if `i` were declared as `Long` as well! – ColinD Mar 04 '11 at 21:39
  • @ColinD: I tested it. 32.6 seconds to run the code above, and 59.9 seconds if `i` is also `Long`. Not quite double. Ouch. – Eddie Mar 04 '11 at 23:57
  • 1
    to clarify: Unfortunately there are no methods that allow arithmetic operations on Int, Long, etc. - so there is no other solution than (un)boxing. Even if there were, they would just use primitive types internally. – Dave O. Mar 05 '11 at 02:12
  • In the class biziclop, aren't the third and fourth statements wrongly inconsistent in their result? Right now the code says `Integer.valueOf(5) == Integer.valueOf(5)` resolves `true` but `Integer.valueOf(500) == Integer.valueOf(500)` resolves `false`. – John K Mar 05 '11 at 02:57
  • 5
    @John: Nope. They're right. ;) It might depend on the specific JVM, but some JVMs optimize by creating flyweights for the autoboxed primitive numbers around zero. – TREE Mar 05 '11 at 03:28
  • 2
    @John yeah it's crazy. Unfortunately, because people often do quick tests on 1, 5, or other small values, they mistaken believe that any valueOf will equate to true. I've seen it lead to more than a couple bugs from inexperienced programmers. – corsiKa Mar 05 '11 at 04:35
  • 15
    @TREE - the spec actually *requires* VMs to create flyweights within a certain range. But sadly it *allows* them to extend that range, meaning that programs may behave differently on different VMs. So much for cross platform... – Daniel Earwicker Mar 05 '11 at 13:38
  • 12
    Java has gone down the drain, with more and more bad design choices. Autoboxing is a complete failure, it's neither robust, predictable or portable. I really really wonder what they were thinking ... instead of fixing the dreaded primitive-object duality they managed to make it worse than in the first place. – Pop Catalin Mar 06 '11 at 18:33
  • 34
    @Catalin I disagree with you that autoboxing is a complete failure. It has some flaws, which is no different than any other design that could have been used (including nothing.) They make very clear what you can and can't expect, and like any other design they expect developers to know and obey the contracts of those designs. – corsiKa Mar 06 '11 at 20:32
  • I'm surprised recent versions of Hotspot haven't narrowed the gap between the boxed type and primitive performance. On my machine the primitive version of the code above is 11 times faster. – CromTheDestroyer Apr 13 '11 at 03:25
  • You would think it would be fairly straightforward to optimize out. I can't imagine what it could be preventing them from doing so. – corsiKa Apr 13 '11 at 14:43
  • 11
    @NaftuliTzviKay That's not a "failure." They make it [**VERY CLEAR**](http://docs.oracle.com/javase/7/docs/technotes/guides/language/autoboxing.html) that the `==` operator performs reference identity comparisons on `Integer` expressions and value equality comparisons on `int` expressions. `Integer.equals()` exists for this very reason. You should **never** use `==` to compare values in any non-primitive type. This is Java 101. – NullUserException May 09 '14 at 03:34
  • @NullUserException I think Naftuli means it's a failure on the part of the programmer who does something like this (and I bet it happens a lot!) – corsiKa Oct 16 '14 at 20:20
  • 1
    The trouble lies it's all very well to blame the programmer and obviously it's very quick to understand the issue if you're looking for it but if you dip in and out of several different languages often months or even years apart each with different naming conventions etc. it's very easy to make mistakes like this and not realise for some time that there is even an issue there. – Chris Nevill Oct 19 '14 at 12:00
  • 1
    I think, the equality issue should come first. Further, boxed types imply the possibility of being `null`. The performance differences can be devastating, but still, the semantic issues are worse. – Holger May 26 '21 at 09:08
92

Autounboxing can lead to hard to spot NPEs

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

In most situations the null assignment to in is a lot less obvious than above.

Heiko Rupp
  • 30,426
  • 13
  • 82
  • 119
48

Boxed types have poorer performance and require more memory.

Brandon Frohbieter
  • 17,563
  • 3
  • 40
  • 62
42

Primitive types:

int x = 1000;
int y = 1000;

Now evaluate:

x == y

It's true. Hardly surprising. Now try the boxed types:

Integer x = 1000;
Integer y = 1000;

Now evaluate:

x == y

It's false. Probably. Depends on the runtime. Is that reason enough?

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • 1
    Reason: If we use Wrapper classes to create variable , it will always create a new reference in the memory – Teja MS Feb 07 '21 at 12:05
40

Besides performance and memory issues, I'd like to come up with another issue: The List interface would be broken without int.
The problem is the overloaded remove() method (remove(int) vs. remove(Object)). remove(Integer) would always resolve to calling the latter, so you could not remove an element by index.

On the other hand, there is a pitfall when trying to add and remove an int:

final int i = 42;
final List<Integer> list = new ArrayList<Integer>();
list.add(i); // add(Object)
list.remove(i); // remove(int) - Ouch!
xehpuk
  • 7,814
  • 3
  • 30
  • 54
  • 11
    It would be broken, yes. However, remove(int) is a design flaw IMO. Method names should never be overloaded if there is the slightest chance of a mix up. – MrBackend Feb 09 '15 at 08:55
  • 5
    @MrBackend Fair enough. Interestingly, `Vector` had `removeElementAt(int)` from the beginning. `remove(int)` was introduced with the collections framework in Java 1.2. – xehpuk Feb 09 '15 at 09:41
  • 8
    @MrBackend: when the `List` API was designed, neither Generics nor Autoboxing existed, so there was no chance of mixing up `remove(int)` and `remove(Object)`… – Holger Jul 21 '15 at 10:22
  • 1
    @Franklin Yu: sure, but when designing a new language/ version without compatibility constraints, you would not stop at changing that unfortunate overload. You would simply get rid of the distinction of primitives and boxed values altogether, so that the question which to use will never appear. – Holger May 09 '16 at 09:19
32

Can you really imagine a

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

loop with java.lang.Integer instead? A java.lang.Integer is immutable, so each increment round the loop would create a new java object on the heap, rather than just increment the int on the stack with a single JVM instruction. The performance would be diabolical.

I would really disagree that it's much mode convenient to use java.lang.Integer than int. On the contrary. Autoboxing means that you can use int where you would otherwise be forced to use Integer, and the java compiler takes care of inserting the code to create the new Integer object for you. Autoboxing is all about allowing you to use an int where an Integer is expected, with the compiler inserting the relevant object construction. It in no way removes or reduces the need for the int in the first place. With autoboxing you get the best of both worlds. You get an Integer created for you automatically when you need a heap based java object, and you get the speed and efficiency of an int when you are just doing arithmetic and local calculations.

Tom Quarendon
  • 5,625
  • 5
  • 23
  • 30
19

Primitive types are much faster:

int i;
i++;

Integer (all Numbers and also a String) is an immutable type: once created it can not be changed. If i was Integer, than i++ would create a new Integer object - much more expensive in terms of memory and processor.

Peter Knego
  • 79,991
  • 11
  • 123
  • 154
  • You don't want one variable to change if you do `i++` on another variable, so Integer quite needs to be immutable to be able to do this (or at least this `i++` would have to create a new Integer object anyway). (And the primitive values are immutable, too - you just don't remark this since they are no objects.) – Paŭlo Ebermann Mar 04 '11 at 22:00
  • 4
    @Paŭlo: Saying that primitive values are immutable is kind of meaningless. When you reassign a primitive variable to a new value, you are not creating anything new. No memory allocation is involved. Peter's point stands: i++ for a primitive does no memory allocation, but for an object it necessarily does. – Eddie Mar 05 '11 at 00:09
  • @Eddie: (It does not necessarily need memory allocation, it could also return a cached value. For some small values it does, I think.) My point was that the immutability of Integers here is not the deciding point, you would anyway want to have another object, regardless of immutability. – Paŭlo Ebermann Mar 05 '11 at 00:15
  • @Paŭlo: my only point was that Integer is an order of magnitude slower then primitives. And this is due to the fact that boxed types are immutable and every time you have change a value a new object is created. I did not claim there is something wrong with them or the fact that they are immutable. Just that they are slower and that a coder should know that. Take a look at how Groovy fares without primitive types http://www.jroller.com/rants/entry/why_is_groovy_so_slow – Peter Knego Mar 05 '11 at 07:34
  • 1
    Immutability and `++` is a red herring here. Imagine Java was enhanced to support operator overloading in a really simple way, such that if a class (such as `Integer` has a method `plus`, then you could write `i + 1` instead of `i.plus(1)`. And assume also that the compiler is smart enough to expand `i++` into `i = i + 1`. Now you could say `i++` and effectively "increment the variable i" without `Integer` being mutable. – Daniel Earwicker Mar 22 '11 at 10:25
16

First and foremost, habit. If you've coded in Java for eight years, you accumulate a considerable amount of inertia. Why change if there is no compelling reason to do so? It's not as if using boxed primitives comes with any extra advantages.

The other reason is to assert that null is not a valid option. It would be pointless and misleading to declare the sum of two numbers or a loop variable as Integer.

There's the performance aspect of it too, while the performance difference isn't critical in many cases (though when it is, it's pretty bad), nobody likes to write code that could be written just as easily in a faster way we're already used to.

biziclop
  • 48,926
  • 12
  • 77
  • 104
  • 16
    I disagree. The performance aspect can be critical. Very little of this is probably inertial or force of habit. – Eddie Mar 04 '11 at 21:26
  • 7
    @Eddie It can be, but it very rarely is. Trust me, for most people performance arguments are just an excuse. – biziclop Mar 04 '11 at 21:35
  • 3
    I too would like to protect the performance argument. On Android with Dalvik each object you create will increase the "risk" of GC being called and the more objects you have the pauses will be longer. So creating Integers instead of int in a loop will probably cost you some dropped frames. – Igor Čordaš Jul 14 '14 at 22:35
  • 2
    @PSIXO It's a fair point, I wrote it with purely server-side Java in mind. Mobile devices are a whole different animal. But my point was that even developers who otherwise write terrible code without any regard to performance will cite this as a reason, from them this sounds very much as an excuse. – biziclop Jul 14 '14 at 23:00
12

By the way, Smalltalk has only objects (no primitives), and yet they had optimized their small integers (using not all 32 bits, only 27 or such) to not allocate any heap space, but simply use a special bit pattern. Also other common objects (true, false, null) had special bit patterns here.

So, at least on 64-bit JVMs (with a 64 bit pointer namespace) it should be possible to not have any objects of Integer, Character, Byte, Short, Boolean, Float (and small Long) at all (apart from these created by explicit new ...()), only special bit patterns, which could be manipulated by the normal operators quite efficiently.

Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
  • I should have said "some implementations", as this is not governed by the language specifications, I think. (And sadly I can't cite any sources here, it is only from what I heard somewhere.) – Paŭlo Ebermann Mar 05 '11 at 11:08
  • ŭlo, JIT already keeps meta into in the pointer; incl, the pointer can keep GC info, or the Klass (optimizing Class is a lot better idea than optimizing Integers, for which I can care less). Changing the pointer would require, shift/cmp/jnz code (or something like that) before each pointer load. The branch probably won't be very well predicted by the hardware (since it can be both Value Type and normal Object) and it would lead to performance hit. – bestsss Mar 11 '11 at 06:24
  • 3
    I did Smalltalk for some years. The optimization still was pretty expensive, as for each operation on an int they had to unmask and reapply them. Currently java is on par with C when manipulating primitive numbers. With unmask+mask it will likely be >30% slower. – R.Moeller Jun 04 '14 at 22:27
9

I can't believe no one has mentioned what I think is the most important reason: "int" is so, so much easier to type than "Integer". I think people underestimate the importance of a concise syntax. Performance isn't really a reason to avoid them because most of the time when one is using numbers is in loop indexes, and incrementing and comparing those costs nothing in any non-trivial loop (whether you're using int or Integer).

The other given reason was that you can get NPEs but that's extremely easy to avoid with boxed types (and it is guaranteed to be avoided as long as you always initialize them to non-null values).

The other reason was that (new Long(1000))==(new Long(1000)) is false, but that's just another way of saying that ".equals" has no syntactic support for boxed types (unlike the operators <, >, =, etc), so we come back to the "simpler syntax" reason.

I think Steve Yegge's non-primitive loop example illustrates my point very well: http://sites.google.com/site/steveyegge2/language-trickery-and-ejb

Think about this: how often do you use function types in languages that have good syntax for them (like any functional language, python, ruby, and even C) compared to java where you have to simulate them using interfaces such as Runnable and Callable and nameless classes.

CromTheDestroyer
  • 3,616
  • 3
  • 20
  • 26
8

Couple of reasons not to get rid of primitives:

  • Backwards compatability.

If it's eliminated, any old programs wouldn't even run.

  • JVM rewrite.

The entire JVM would have to be rewritten to support this new thing.

  • Larger memory footprint.

You'd need to store the value and the reference, which uses more memory. If you have a huge array of bytes, using byte's is significantly smaller than using Byte's.

  • Null pointer issues.

Declaring int i then doing stuff with i would result in no issues, but declaring Integer i and then doing the same would result in an NPE.

  • Equality issues.

Consider this code:

Integer i1 = 5;
Integer i2 = 5;

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

Would be false. Operators would have to be overloaded, and that would result in a major rewrite of stuff.

  • Slow

Object wrappers are significantly slower than their primitive counterparts.

Illidanek
  • 996
  • 1
  • 18
  • 32
Anubian Noob
  • 13,426
  • 6
  • 53
  • 75
7

Objects are much more heavyweight than primitive types, so primitive types are much more efficient than instances of wrapper classes.

Primitive types are very simple: for example an int is 32 bits and takes up exactly 32 bits in memory, and can be manipulated directly. An Integer object is a complete object, which (like any object) has to be stored on the heap, and can only be accessed via a reference (pointer) to it. It most likely also takes up more than 32 bits (4 bytes) of memory.

That said, the fact that Java has a distinction between primitive and non-primitive types is also a sign of age of the Java programming language. Newer programming languages don't have this distinction; the compiler of such a language is smart enough to figure out by itself if you're using simple values or more complex objects.

For example, in Scala there are no primitive types; there is a class Int for integers, and an Int is a real object (that you can methods on etc.). When the compiler compiles your code, it uses primitive ints behind the scenes, so using an Int is just as efficient as using a primitive int in Java.

Umesh K
  • 13,436
  • 25
  • 87
  • 129
  • 1
    I would have assumed that the JRE would be "smart" enough to do this with Java wrapped primitives as well. Fail. – Naftuli Kay Mar 04 '11 at 21:26
7

In addition to what others have said, primitive local variables are not allocated from the heap, but instead on the stack. But objects are allocated from the heap and thus have to be garbage collected.

Eddie
  • 53,828
  • 22
  • 125
  • 145
  • 3
    Sorry, this is wrong. A smart JVM can do escape analysis on any object allocations and, if they cannot escape, allocate them on the stack. – rlibby Mar 04 '11 at 21:31
  • 2
    Yes, this is *beginning* to be a feature of modern JVMs. In five years, what you say will be true for most JVMs then in use. Today it is not. I almost commented about this, but decided to not comment on it. Perhaps I should have said something. – Eddie Mar 04 '11 at 21:33
5

Primitive types have many advantages:

  • Simpler code to write
  • Performance is better since you are not instantiating an object for the variable
  • Since they do not represent a reference to an object there is no need to check for nulls
  • Use primitive types unless you need to take advantage of the boxing features.
Bentaye
  • 9,403
  • 5
  • 32
  • 45
Adriana
  • 71
  • 1
  • 4
5
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));

Milliseconds taken to loop '100000000' times around Long: 468

Milliseconds taken to loop '100000000' times around long: 31

On a side note, I wouldn't mind seeing something like this find it's way into Java.

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

Where the for loop automatically increments loop1 from 0 to 1000 or

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

Where the for loop automatically decrements loop1 1000 to 0.

Anubian Noob
  • 13,426
  • 6
  • 53
  • 75
Keith
  • 51
  • 1
  • 2
5

It's hard to know what kind of optimizations are going on under the covers.

For local use, when the compiler has enough information to make optimizations excluding the possibility of the null value, I expect the performance to be the same or similar.

However, arrays of primitives are apparently very different from collections of boxed primitives. This makes sense given that very few optimizations are possible deep within a collection.

Furthermore, Integer has a much higher logical overhead as compared with int: now you have to worry about about whether or not int a = b + c; throws an exception.

I'd use the primitives as much as possible and rely on the factory methods and autoboxing to give me the more semantically powerful boxed types when they are needed.

Matthew
  • 44,826
  • 10
  • 98
  • 87
2
  1. You need primitives for doing mathematical operations
  2. Primitives takes less memory as answered above and better performing

You should ask why Class/Object type is required

Reason for having Object type is to make our life easier when we deal with Collections. Primitives cannot be added directly to List/Map rather you need to write a wrapper class. Readymade Integer kind of Classes helps you here plus it has many utility methods like Integer.pareseInt(str)

Bentaye
  • 9,403
  • 5
  • 32
  • 45
Prabakaran
  • 128
  • 1
  • 9
2

I agree with previous answers, using primitives wrapper objects can be expensive. But, if performance is not critical in your application, you avoid overflows when using objects. For example:

long bigNumber = Integer.MAX_VALUE + 2;

The value of bigNumber is -2147483647, and you would expect it to be 2147483649. It's a bug in the code that would be fixed by doing:

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

And bigNumber would be 2147483649. These kind of bugs sometimes are easy to be missed and can lead to unknown behavior or vulnerabilities (see CWE-190).

If you use wrapper objects, the equivalent code won't compile.

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

So it's easier to stop these kind of issues by using primitives wrapper objects.

Your question is so answered already, that I reply just to add a little bit more information not mentioned before.

Fernando Garcia
  • 1,946
  • 2
  • 12
  • 14
1

Because JAVA performs all mathematical operations in primitive types. Consider this example:

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

Here, reminder and unary plus operations can not be applied on Integer(Reference) type, compiler performs unboxing and do the operations.

So, make sure how many autoboxing and unboxing operations happen in java program. Since, It takes time to perform this operations.

Generally, it is better to keep arguments of type Reference and result of primitive type.

Anubian Noob
  • 13,426
  • 6
  • 53
  • 75
user3462649
  • 1,459
  • 3
  • 13
  • 18
1

The primitive types are much faster and require much less memory. Therefore, we might want to prefer using them.

On the other hand, current Java language specification doesn’t allow usage of primitive types in the parameterized types (generics), in the Java collections or the Reflection API.

When our application needs collections with a big number of elements, we should consider using arrays with as more “economical” type as possible.

*For detailed info see the source: https://www.baeldung.com/java-primitives-vs-objects

Arun Joseph
  • 2,736
  • 25
  • 35
0

To be brief: primitive types are faster and require less memory than boxed ones

White Link
  • 106
  • 7