Introduction
Apache Commons Lang Mutable provides mutable access to a value. Typically, we can use this to make a primitive value in Java as a mutable value. In this post, we will learn about the Apache Commons Lang Mutable Class.
Primitive wrapper class in Java
In Java, there are primitive wrapper classes for each of the primitive values as shown:
- Integer for int
- Long for long
- Float for float
- Double for double
- Boolean for boolean
- Character for char
- Byte for byte
- Short for short
Each of these classes acts as a wrapper over the corresponding primitive type. We can this think of these value-based classes as the class version of the primitive types.
Autoboxing (boxing) and unboxing
Autoboxing or simply boxing is a term we use to refer to the conversion of a primitive value into its corresponding wrapper type. As shown below, when we do the assignment in line 2, an Integer wrapper class instance would be automatically created (which is an equivalent of Integer.valueOf(number)
).
int number = 5;
Integer integerObj = number; //autoboxing
System.out.println(integerObj); //5
Similarly, unboxing is when we convert a wrapper class object into primitive type. Again, this is done automatically for us. This is equivalent to calling intValue() method on the Integer object to get the primitive int value.
int newNumber = integerObj; //unboxing
System.out.println(newNumber);
Mutability of primitive wrapper class
These wrapper classes are final and immutable. In other words, once we have an instance of a primitive wrapper class, we cannot mutate or change the primitive value it holds. When we perform an addition or postfix increment, it will create a new instance.
Let us confirm this by checking the hashCode of the Integer instance by calling System.identityHashCode().
There is a common misunderstanding that the Hex value the hashCode() method returns is the internal address at which the object is stored. This is wrong. How the default hashCode is computed is an implementation detail (native code) and has nothing to do with the memory address.
As per the contract of hashCode, it states,
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
Since the Integer class has overridden hashCode to return the primitive int value it holds, we can use the System.identityHashCode method here. It will return the hashCode that would be returned by the default hashCode implementation.
int number = 5;
Integer integerObj = number;
System.out.println(System.identityHashCode(integerObj)); //1712121640
integerObj++;
System.out.println(integerObj); //6
System.out.println(System.identityHashCode(integerObj)); //862544440
Note: The value of the hash code you get could very well be different from what I have got.
In the above code, when we incremented the value of the integerObj, it would have created a new Integer object and set its value to 6. We can see that the identity hash codes of these two are different.
An important point is that the identity hash code will not change even if the data or fields used in the equals or hashCode changes. It would be computed once for an object and stored in the object’s header (this is independent of the object’s location or the object’s data). Hence, if it had mutated the same Integer instance, we would have got the same identity hash code. Since we didn’t, we can conclude that a new Integer instance was created to hold the updated value.
Checking reference of Integer instance
Another simple way to verify that a new wrapper instance would be created is to store the reference of the Integer instance in a different variable and check against that after the modification.
int number = 5;
Integer integerObj = number;
Integer oldIntegerObj = integerObj;
integerObj++;
System.out.println(integerObj); //6
System.out.println(oldIntegerObj == integerObj); //false
The reference held by oldIntegerObj wasn’t equal to the reference pointed by integerObj after the postfix increment.
Primitive wrapper classes in Collections and Maps
There are scenarios where we cannot use primitive types. For example, when using primitive types in a Collection (like a List or a Set) or in a Map because we cannot specify primitive type as type parameters. Hence, in these situations, we have to use the wrapper class type, which corresponds to the primitive type.
When there is a need for a frequent update of these values used in a Collection or Map, then there will be a lot of boxing to convert the primitive value to the wrapper type. Depending on how much of this is happening, it could lead to a degrade in the performance of your application.
public class MyClass {
private final Map<String, Long> map = new HashMap<>();
public void update(String key, long value) {
map.put(key, value);
}
}
In the above example, if there is a very high update volume, then for each update call, we must box the primitive long into its wrapper type (Long).
Apache Commons Lang Mutable Interface
The Mutable interface from the Apache Commons Lang provides mutable access to a primitive value. The interface has two main method viz., getValue() and setValue()
public interface Mutable<T> {
T getValue();
void setValue(T value);
}
There are eight subclasses of Mutable viz.,
- MutableInt
- MutableLong
- MutableFloat
- MutableDouble
- MutableBoolean
- MutableByte
- MutableShort
- MutableObject
There is no Mutable subclass for character.
Except MutableObject and MutableBoolean, all the classes extend the Number class. Hence, we can use these in place of a Number. But since they don’t extend the wrapper classes (because the wrapper types are final), we cannot use these in place of them (e.g., we cannot use a MutableInt in place of an Integer).
Apache Commons Lang Mutable - Use case
Since the mutable interface offers the setValue method, we can pass the new value (primitive value) and update the existing instance.
But how can we avoid auto-boxing if the type is T? Wouldn’t there be boxing to convert the primitive value to the corresponding boxed type?
Each of the Mutable’s subclasses corresponding to the primitive values has a separate setValue method which takes the primitive value. For example, MutableInt has,
public void setValue(final int value) {
this.value = value;
}
And MutableDouble has,
public void setValue(final double value) {
this.value = value;
}
and so on..
Hence, when we have a use case of frequently updating primitive value in a collection or a map, we can replace the primitive wrapper class with a Mutable instance and update the Mutable value using the setValue method. This will avoid the need for auto-boxing and hence the need to create new wrapper objects for each update.
Using Apache Commons Lang Mutable (MutableLong) - Example
Let us update the initial code to use a MutableLong in place of Long value.
public class MyClassUsingMutableValue {
private final Map<String, MutableLong> map = new HashMap<>();
public void update(String key, long value) {
if (map.containsKey(key)) {
map.put(key, new MutableLong(value));
} else {
map.get(key).setValue(value);
}
}
}
Since the type of the value is different now (MutableLong), we cannot directly use the Map’s put method. Instead, we first check if the map has a value (a MutableLong instance) for the key. If no, then we create a new MutableLong value with the passed value. Or, we get the existing instance and update its value using the setValue method.
We can simplify this using computeIfAbsent method.
public void update(String key, long value) {
map.computeIfAbsent(key, k -> new MutableLong())
.setValue(value);
}
The mappingFunction passed will be executed when the key is seen for the first time. In other words, if the key is new, then it will create a new MutableLong instance. The computeIfAbsent returns the mapping for the passed key (either the newly created instance or the existing value) and we call setValue to update the value of the MutableLong.
Comparing the performance of Apache Commons Lang Mutable vs normal boxing
We will use JMH (Java Microbenchmark Harness) to benchmark and to compare the performance of boxing vs MutableLong. I’ll not explain how JMH works in this post. You can go through the linked JMH post to understand JMH.
Measuring the performance of auto-boxing
First, let us measure the performance when we perform auto-boxing i.e., MyClass class.
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Warmup;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class BoxingBenchmark {
private static final Random RANDOM = new Random();
private static final MyClass MY_CLASS = new MyClass();
@Benchmark
@Fork(value = 2)
@Measurement(iterations = 10, time = 1)
@Warmup(iterations = 5, time = 1)
public void updateWithAutoBoxing() {
for (int i = 0; i < 1000; i++) {
long longVal = RANDOM.nextLong();
MY_CLASS.update("some-key", longVal);
}
}
}
We’ve configured this test to measure the average time in milliseconds. The test results will be ms/op i.e., number of milliseconds taken per operation. We will use two forks with 5 iterations for warmup and 10 iterations of actual measurement. We generate a random long and update a single key in the map of MyClass. The for-loop runs for 1000 times.
Running the benchmark, we get (details on how to setup and run can be found in the JMH post)
java -jar target/benchmarks.jar BoxingBenchmark
We get the result as 0.028 ms/op.
Benchmark Mode Samples Score Score error Units
c.t.m.BoxingBenchmark.updateWithAutoBoxing avgt 20 0.028 0.001 ms/op
Repeating the experiment for 10K, 100K and 1M times (the number of times the for-loop runs), the results are as follows:
Number of times | ms/op |
---|---|
1K | 0.028 ms/op |
10K | 0.241 ms/op |
100K | 2.634 ms/op |
1M | 27.125 ms/op |
Measuring the performance of MutableLong
Let us now measure the performance of MutableLong i.e., MyClassUsingMutableValue class. The benchmark code is shown below. It is similar to the previous version except that we use MyClassUsingMutableValue in place of MyClass.
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Warmup;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class MutableBenchmark {
private static final Random RANDOM = new Random();
private static final MyClassUsingMutableValue MY_CLASS_USING_MUTABLE_VALUE = new MyClassUsingMutableValue();
@Benchmark
@Fork(value = 2)
@Measurement(iterations = 10, time = 1)
@Warmup(iterations = 5, time = 1)
public void updateWithMutableLong() {
for (int i = 0; i < 1000; i++) {
long longVal = RANDOM.nextLong();
MY_CLASS_USING_MUTABLE_VALUE.update("some-key", longVal);
}
}
}
The results of running it for 1K, 10K, 100K and 1M times are:
Number of times | ms/op | %improvement |
---|---|---|
1K | 0.017 ms/op | 39.28% improvement |
10K | 0.172 ms/op | 28.6% improvement |
100K | 1.768 ms/op | 32.87% improvement |
1M | 16.756 ms/op | 38.22% improvement |
The above table also shows the percentage improvement when compared to the auto-boxing performance. This clearly shows the benefits in performance when using MutableLong (or any Mutable class) when compared to auto-boxing.
Apache Commons Lang Mutable* class methods
So far we have used the setValue method of the MutableLong class and have seen the performance improvement over normal Long with auto-boxing. Let us now look into the other methods in the Mutable subclasses. I’ll show the methods only from MutableInt class. I’ll skip the rest as the other classes have similar methods.
MutableInt class
There are four overloaded constructors of MutableInt:
- MutableInt() - Constructs a MutableInt with default value of zero.
- MutableInt(int value) - Constructs a MutableInt with the passed int value.
- MutableInt(Number value) - Constructs a MutableInt with the passed Number.
- MutableInt(String value) - Constructs a MutableInt with the passed string value. It will throw a NumberFormatException if it cannot parse the string to an int.
MutableInt methods
Let us start with a MutableInt with value of 5.
MutableInt mutableInt = new MutableInt(5);
MutableInt - add, subtract methods
The methods which we will see are:
- add - It takes an int or a Number and adds that value to the MutableInt instance.
- addAndGet - It takes an int or a Number and increments this instance’s value by the passed operand. It will return the value associated with the instance after the addition operation.
- getAndAdd - It takes an int or a Number and increments this instance’s value by the passed operand. It will return the value associated with the instance before the addition operation.
mutableInt.add(2);
System.out.println(mutableInt); //7
System.out.println(mutableInt.addAndGet(1)); //8
System.out.println(mutableInt.getAndAdd(1)); //8
System.out.println(mutableInt); //9
The subtract method takes an int or a Number and subtracts the value from the MutableInt’s value.
MutableInt mutableInt = new MutableInt(5);
mutableInt.subtract(1);
System.out.println(mutableInt); //4
MutableInt - increment and decrement methods
The increment method increments the value associated with the MutableInt by 1.
The incrementAndGet increments the value by 1 and returns it whereas getAndIncrement also increments the value by 1, but it returns the value prior to the increment.
MutableInt mutableInt = new MutableInt(5);
mutableInt.increment(); //6
System.out.println(mutableInt.incrementAndGet()); //7
System.out.println(mutableInt.getAndIncrement()); //7
System.out.println(mutableInt); //8
Similar to the three increment methods, we have for decrement - decrement, decrementAndGet and getAndDecrement.
MutableInt mutableInt = new MutableInt(5);
mutableInt.decrement();
System.out.println(mutableInt); //4
System.out.println(mutableInt.decrementAndGet()); //3
System.out.println(mutableInt.getAndDecrement()); //3
System.out.println(mutableInt); //2
MutableInt - getValue and other methods
The getValue method returns the value associated with the MutableInt as an Integer. The intValue, longValue, doubleValue and floatValue returns the int value associated with the MutableInt as an int,long, double and float respectively.
Conclusion
In this post, we learnt about the Apache Commons Lang Mutable Class. We saw the problem with auto-boxing and how it impacts performance. We then saw how we can use one of the subclasses of Mutable and measured the performance using JMH.