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:
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:
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.
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:
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:
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!):
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[]!):
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:
@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
GO TO FULL VERSION