Integer and int

    In this topic, I want to describe some basic differences between primitive types and their corresponding object types using the example of int and Integer. These differences are quite simple and, if you think a little, it’s quite logical, but, as experience has shown, the programmer does not always think about it.

    The main difference, of course, is that Integer is a fully functional object that takes up space on the heap, and in the code you use links to it that are implicitly converted to values:
    int a = 1000; // a - число
    Integer b = 1000; // b - ссылка на объект
    When assigning a value to a variable of type Integer, memory is usually allocated on the heap for a new object, as if you wrote new Integer(1000)(the so-called autoboxing ). However, sometimes old objects are reused. This is illustrated by the following code (JDK 1.6):
    Integer a1 = 50;
    Integer a2 = 50;
    Integer b1 = 500;
    Integer b2 = 500;
    System.out.println(a1==a2);
    System.out.println(b1==b2);
    The result of the execution will be:
    true
    false
    Here, the static method java.lang.Integer.valueOf (int) is actually called, which, as you can see in the sources , caches values ​​from -128 to 127 (in a more recent implementation, the upper bound of the cache can be changed).

    However, in most cases a new object is created, and this can be significant. Remember also that an Integer object never changes its value. Consider this seemingly innocent code:
    public class Increment
    {
        public static void main(String[] args)
        {
            Integer a=0;
            while(true) a++;
        }
    }
    Let's profile memory usage, for example, with the trial version of JProfiler :

    Obviously, with each increment, a new Integer object is created, and the old ones are then cleaned up by the garbage collector when they accumulate about one hundred thousand. Good system load for normal increment operation.

    In general, it is clear that Integer should be used only when you cannot do without it. One such example is parameterized types (generics), for example, standard collections. But here you must be careful to use memory wisely. I will give a somewhat exaggerated example based on the problem that I encountered in a real project. In some scientific analysis, it was required to associate long sets of natural numbers with certain objects. You can emulate this with the following code:
    import java.util.*;

    public class MapInteger
    {
        static Map> subSets = new HashMap>();

        public static void put(Integer key, int value)
        {
            if(!subSets.containsKey(key)) subSets.put(key, new HashSet());
            subSets.get(key).add(value);
        }

        public static Collection getRandomKeys()
        {
            List vals = new ArrayList();
            for(int i=0; i<(int)(Math.random()*500); i++)
            {
                vals.add((int)(Math.random()*1000));
            }
            return vals;
        }
        
        public static void main(String[] args)
        {
            new Scanner(System.in).nextLine();
            for(Integer i=0; i<100000; i++)
            {
                for(Integer key: getRandomKeys())
                    put(key, i);
            }
            new Scanner(System.in).nextLine();
        }
    }
    For each number from the first hundred thousand, we define a set of keys using getRandomKeys (in the real task, the keys, of course, were not random) and add the current number to the corresponding sets of subSets. The type of Integer keys is chosen to simplify the illustration; he is generally unimportant. Here are the number of objects before the operation:


    But after:


    Forcing the garbage collector to start helped slightly:


    40 megabytes of memory eats integers - this is sad. The reason lies in the prototype of the put method: Due to the fact that the int type is used here, the values ​​of the variable i are automatically converted to int (unboxing) when transferred to the method, and then again to Integer (boxing), but a new object is already created. Replace the prototype with and run the profiler again. At the beginning, the picture is the same:
    public static void put(Integer key, int value)
    int valueInteger value


    But at the end there are significant differences:


    And after garbage collection:


    So, by replacing one int with Integer, you can save about 40% of the used memory. Note that for(Integer i=0; i<100000; i++)Integer is also used for a reason: we will write an int here, and the first correction will not help. We see that the rule of writing int wherever Integer can be omitted does not always work: you have to think in each case. Sometimes a native implementation of the integer cache may also come in handy.

    Also popular now: