Java data types can be conditionally divided into two blocks: primitive and reference (classes). There are several primitive data types in Java, such as integers (byte, short, int, long), floating point numbers (float, double), logical data type (boolean) and character data type (char). You’ve probably already known that each primitive data type has its own wrapper class. A reference data type that "wraps" or turns its primitive little brother in a Java object. Integer is a wrapper class for its primitive bro named int. Integer in English means a whole number. They can be positive, negative or 0. Unfortunately, Integer in Java does not mean any whole number. Integer in java is a whole number that fits in 32 bits. If you want a larger number you are welcome to use Java Long numbers. They have 64 bits at their disposal. If you are unlucky enough to need an even larger number Java has you covered with BigInteger.

Working with Integer

As a wrapper class, Integer provides various methods for working with int, as well as a number of methods for converting int to String and String to int. The class has two constructors:
  • public Integer(int i), where i is a primitive value to initialise. This one creates an Integer object that is initialised with the int value.

  • public Integer(String s) throws NumberFormatException. Here s is a string representation of the int value. This constructor creates an Integer object that was initialised with the int value provided by string representation.

Integer object creation

There are different Integer object creation options. One of the most commonly used is the easiest one. Here is an example:

Integer myInteger = 5;
The initialization of the Integer variable in this case is similar to the initialization of the primitive int variable. By the way you can initialise an Integer variable with the value of an int. Here is an example:

int myInt = 5;
Integer myInteger = myInt;
System.out.println(myInteger); 
The output here is:
5
In fact, here we can observe auto-packing. Also we can create an Integer object just like any other objects using a constructor and new keyword:

Integer myInteger = new Integer(5);
You can do with the Integer variable the same as with int (add, subtract, multiply, divide, increment, decrement). However, it is important to remember that Integer is a reference data type, and a variable of this type can be null. In this case, it is better to refrain from such operations.

Integer myInteger1  = null;
Integer myInteger2 = myInteger1 + 5; 
Here we’ll get an exception:
Exception in thread "main" java.lang.NullPointerException"

Integer class constants

The Integer class provides various constants and methods for working with integers. Here they are:
  • SIZE means the number of bits in the two-digit number system occupied by the type int

  • BYTES is the number of bytes in two-digit number system occupied by type int

  • MAX_VALUE is the maximum value that the int type can hold

  • MIN_VALUE is the minimum value that the int type can hold

  • TYPE returns an object of type Class from type int

Integer class most useful methods

Now let's take a glimpse on the most used methods of the Integer class. The most popular of them, I presume, are methods for converting a number from a String, or vice versa.
  • static int parseInt(String s) this method converts String to int. If the conversion isn’t possible, NumberFormatException will be thrown.

  • static int parseInt(String s, int radix) this method also converts the s parameter to an int. The radix parameter indicates the number system s was originally written.

In addition to parseInt, there is also a very similar valueOf method in several variations. However, the result of valueOf will be Integer, and parseInt will be int.
  • static Integer valueOf(int i) returns an Integer whose value is i;

  • static Integer valueOf(String s) works like parseInt(String s), but the result will be Integer, not int;

  • static Integer valueOf(String s, int radix) works the same as parseInt(String s, int radix), but the result is an Integer, not an int.

Is there any problem with Integer class? Oh yeah, there is…

So there are two types for integers (that fit into 32 bits) in Java: int and Integer. To understand the specifics of each of them we need to know the following about the JVM memory model: everything you declare is stored either in Stack Memory (JVM Stack specific for each Thread), or Heap Space. Primitive types (int, long, float, boolean, double, char, byte, etc) are stored in Stack memory. All Objects and arrays are stored in Heap Space. References to these objects and arrays needed for the methods are stored in Stack. So. Why do we care? Well, you see, Stack is smaller than Heap (a con), but it is much faster to allocate values in Stack, than in Heap (a pro). Let's start with a primitive type int. It takes up exactly 32 bits. That’s 32/8=4 bytes. Because it is a primitive type. Now, let’s consider Integer. It is an object, with extra overhead and alignments. I have used a library jol to measure its size:

public static void main(String[] args) {
 	System.out.println(ClassLayout.parseInstance(Integer.valueOf(1)).toPrintable());
}
and it turned out to take up 16 bytes:
java.lang.Integer object internals: OFF SZ TYPE DESCRIPTION VALUE 0 8 (object header: mark) 0x000000748c90e301 (hash: 0x748c90e3; age: 0) 8 4 (object header: class) 0x000492a0 12 4 int Integer.value 1 Instance size: 16 bytes
What?! That’s 4 times more memory! But let us not stop there. As Java developers we don’t normally stop using a single integer. What we really want is to use a lot of them. Like in a sequence. For example, in an array. Or a List. Arrays are stored in Heap, as Lists are. So, allocation should take approximately the same amount of time. Right? But what if we need to allocate more memory? Let’s check how much space an array of 1000 primitive int values takes:

public static void main(String[] args) {
    	int[] array = new int[1000];
    	for (int i = 0; i < 1000; i++) array[i] = i;                System.out.println(ClassLayout.parseInstance(array).toPrintable());
}
And the result is 4016 bytes:
OFF SZ TYPE DESCRIPTION VALUE 0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0) 8 4 (object header: class) 0x00006c38 12 4 (array length) 1000 12 4 (alignment/padding gap) 16 4000 int [I.<elements> N/A Instance size: 4016 bytes Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
OK, that does sort of make sense, considering a single int takes 4 bytes. What about an ArrayList<Integer> of a 1000 Integers? Let’s have a look:

public static void main(String[] args) {
	List<Integer> list = new ArrayList<>(1000);
	for (int i = 0; i < 1000; i++) list.add(i);
      System.out.println(GraphLayout.parseInstance(list).toFootprint());
}
And the result is 20040 bytes (again, 4 times more!):
java.util.ArrayList@66d3c617d footprint: COUNT AVG SUM DESCRIPTION 1 4016 4016 [Ljava.lang.Object; 1000 16 16000 java.lang.Integer 1 24 24 java.util.ArrayList 1002 20040 (total)
So, ArrayList<Integer> takes up 4 times more memory space. That’s not good. But still, Lists are easier because we can add and delete elements! Oh Java… Why do you need to box everything?! But, I am forgetting, Java is great, and its greatness lies in the abundance of open source libraries we can use! Trove4j is one of them. It has TIntArrayList which internally has an int[] data. Let’s measure its size:

public static void main(String[] args) {
	TIntList list = new TIntArrayList(1000);
	for (int i = 0; i < 1000; i++) list.add(i);
	System.out.println(GraphLayout.parseInstance(list).toFootprint());
}
And the result is 4040 bytes (almost the same as just int[]!):
gnu.trove.list.array.TIntArrayList@7440e464d footprint: COUNT AVG SUM DESCRIPTION 1 4016 4016 [I 1 24 24 gnu.trove.list.array.TIntArrayList 2 4040 (total)
So, in the end, we can have the best of both worlds! Lists of integers which take 4 times less space. And this does not involve Integer instances. Only ints. We Java devs really care about memory… But we also care about performance. There’s a wonderful microbenchmarking library with a modest name jmh that lets us measure performance of code. First let's compare performance of calculating a sum of two random integers, boxed, or not: The configuration for jmh is as follows:

benchmark {
	configurations {
    	main {
        	warmups = 5 // number of warmup iterations
        	iterations = 50 // number of iterations
        	iterationTime = 500 // time in seconds per iteration
        	iterationTimeUnit = "ns" // time unit for iterationTime
The benchmarks:

private static final Random random = new Random();

@Benchmark
public int testPrimitiveIntegersSum() {
	int a = random.nextInt();
	int b = random.nextInt();
	return a + b;
}

@Benchmark
public Integer testBoxedIntegersSum() {
	Integer a = random.nextInt();
	Integer b = random.nextInt();
	return a + b;
}
The results:
main: test.SampleJavaBenchmark.testBoxedIntegersSum 5693337.344 ±(99.9%) 1198774.178 ops/s [Average] (min, avg, max) = (1092314.989, 5693337.344, 12001683.428), stdev = 2421583.144 CI (99.9%): [4494563.166, 6892111.522] (assumes normal distribution) main: test.SampleJavaBenchmark.testPrimitiveIntegersSum 15295010.959 ±(99.9%) 2555447.456 ops/s [Average] (min, avg, max) = (4560097.059, 15295010.959, 24283809.447), stdev = 5162130.283 CI (99.9%): [12739563.502, 17850458.415] (assumes normal distribution)
So, on average, allocation, and sum of primitive ints is more than twice as fast as that of boxed Integers. Now, let's compare performance of creation and calculation of sum of collections (or arrays of 1000 ints of Integers):

@Benchmark
public int testPrimitiveArray() {
	int[] array = new int[1000];
	for (int i = 0; i < 1000; i++) array[i] = i;
	int sum = 0;
	for (int x : array) sum += x;
	return sum;
}
11933.545 ops/s [Average]


@Benchmark
public int testBoxesArray() {
	Integer[] array = new Integer[1000];
	for (int i = 0; i < 1000; i++) array[i] = i;
	int sum = 0;
	for (int x : array) sum += x;
	return sum;
}
2733.312 ops/s [Average]


@Benchmark
public int testList() {
	List<Integer> list = new ArrayList<>(1000);
	for (int i = 0; i < 1000; i++) list.add(i);
	int sum = 0;
	for (int x : list) sum += x;
	return sum;
}
2086.379 ops/s [Average]


@Benchmark
public int testTroveIntList() {
	TIntList list = new TIntArrayList(1000);
	for (int i = 0; i < 1000; i++) list.add(i);
	int sum = 0;
	for (int i = 0; i < 1000; i++) sum += list.get(i);
	return sum;
}
5727.979 ops/s [Average]
The results: primitives array is more than 4 times faster than array of boxed values (Integers); almost six times faster than ArrayList of boxed values (Integers); and twice as fast as a TIntArrayList (which actually decorates an array of primitive ints). Therefore, if you need a data-structure to store a collection of integer values, and its size is not going to change, use an int[]; if the size is going to change — you might want to use the tove4j library with TIntArrayList. And here comes the end of my essay where I explain the cons of using Integer type. There are some interesting static methods of Integer, which I should talk about before I finish. public static Integer getInteger(String nm, int val) doesn’t do what one might think, but retrieves an Integer value of a system property. Val is the default in case this property is not set. public static String toBinaryString(int i) returns a String with a binary representation of a number. There are methods for retrieval of a based-16 (toHexString) and based-8 (toOctalString) representations. There is a method to parse a String into an int. Even if the string is a non-10 radix based representation. Here are some examples: Integer.parseInt("-FF", 16) returns -255 Integer.parseInt("+42", 10) returns 42 Integer.parseInt("1100110", 2) returns 102 Java.lang.Integer Class - 1