As you know, in Java there are primitive types for numbers (byte, short, int, long, float, double) and object wrappers over them (Byte, Short, Integer, Long, Float, Double). In various articles you can find diametrically opposite recommendations about what to use. On the one hand, object wrappers are universal: they can be used with standard collections that are comfortable, encapsulated, and generally beautiful. But boxing kills performance and eats a bunch of memory. Primitive types are fast and compact, but they can be placed only in arrays that cannot be protected from writing, and the abstraction is at zero. If you need something like Map to display something on numbers, you will either have to put up with a loss of performance and memory, or use third-party libraries that implement a non-standard interface. However, in some cases mutable numbers will help you.
Imagine that you need to count the number of different lines that come from somewhere. Often write something like this code:
public Map<String, Integer> countStrings() { Map<String, Integer> counts = new HashMap<String, Integer>(); while(true) { String next = getNextString(); if(next == null) break; Integer val = counts.get(next); if(val == null) counts.put(next, 1); else counts.put(next, val+1); } return counts; }
If there are not so many types of strings, and there are enough repetitions, then boxing will create millions of temporary Integer objects, which the garbage collector will clean up later. Imagine scary. No, it is not catastrophically slow, but it’s still easy to speed up the procedure 2-3 times. For this we use MutableInteger.
Such a class is in some libraries (for example, in
org.apache.commons.lang ), but it is easy to write it yourself. A simple implementation might look something like this:
')
public class MutableInteger { private int value; public MutableInteger(int value) { this.value = value; } public int intValue() { return value; } public void set(int value) { this.value = value; } public void increment() { value++; } public String toString() { return String.valueOf(value); } }
You can then add simple methods for arithmetic operations. It is also convenient to inherit a Number, implement the Comparable interface, and also not forget about equals and hashCode (in hashCode, you can simply return value). But for the current task we have enough of what is written. Now countStrings () can be rewritten as follows:
public Map<String, MutableInteger> countStrings() { Map<String, MutableInteger> counts = new HashMap<String, MutableInteger>(); while(true) { String next = getNextString(); if(next == null) break; MutableInteger val = counts.get(next); if(val == null) counts.put(next, new MutableInteger(1)); else val.increment(); } return counts; }
It’s not very good, of course, to disclose the implementation details, returning Map <String, MutableInteger>, but if we inherit java.lang.Number, then we can return Map <String, Number>. Well, or in extreme cases, after the calculation, copy everything to a new Map. Similarly, you can collect not only the number, but also other statistics on the set of objects.
Note also that java.util.concurrent.atomic.AtomicInteger is essentially also MutableInteger, but the atomic overhead may even exceed the costs of creating objects and garbage collection from the first example, so a separate MutableInteger class is still needed.