Problem
There is much debate online about the merits of using the BigDecimal.equals(Object) method. In the equals method, scale is considered in such a way that 1.0 != 1.00. The compareTo method, however, ignores scale, meaning that 1.0 == 1.00. In this specific case, however, scale is known, so either method will work. Out of curiosity, I wondered if there was a performance hit in either method.
Approach
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.security.SecureRandom;
import org.joda.time.DateTime;
import org.junit.Test;
/**
* <p>
* Class to test the performance of {@link BigDecimal#equals(Object)} vs.
* {@link BigDecimal#compareTo(BigDecimal)}. The question at hand is which
* method to use when the precision and scale of the BigDecimals being compared
* is know to be the same.
* </p>
* <p>
* In the equals method, scale is considered in such a way that 1.0 != 1.00. The
* compareTo method, however, ignores scale, meaning that 1.0 == 1.00. In this
* specific case, however, scale is known, so either method will work. Out of
* curiosity, I wondered if there was a performance hit in either method.
* </p>
*/
public class BigDecimalPerformanceTest {
/** The {@link SecureRandom} object. */
public static final SecureRandom RANDOM = new SecureRandom();
/** The number of times to loop over each comparison. */
public static final int ITERATIONS = 100000000;
/** The number of times to loop over all comparisons. */
public static final int LOOPS = 3;
/** BigDecimal precision. */
private static final int PRECISION = 15;
/** BigDecimal scale. */
private static final int SCALE = 2;
/** Equals vs. compareTo. */
@SuppressWarnings("unused")
@Test
public void equalsVsCompareTo() {
for (int j = 0; j < LOOPS; j++) {
/* Reference object to do all the comparisons against. */
BigDecimal testBigDecimal = makeBigDecimal();
/*
* Classic equals method. Assigning results to a variable for
* consistency.
*/
DateTime startTime = new DateTime();
for (int i = 0; i < ITERATIONS; i++) {
boolean test = testBigDecimal.equals(makeBigDecimal());
}
DateTime endTime = new DateTime();
System.out.println("BigDecimal.equals() : "
+ endTime.minus(startTime.getMillis()).toString("m:s.SSS"));
/* Plain compareTo method. */
startTime = new DateTime();
for (int i = 0; i < ITERATIONS; i++) {
int test = testBigDecimal.compareTo(makeBigDecimal());
}
endTime = new DateTime();
System.out.println("BigDecimal.compareTo()1: "
+ endTime.minus(startTime.getMillis()).toString("m:s.SSS"));
/*
* Convert compareTo to true/false as a closer comparison with
* equals as far as actual usage would be concerned.
*/
startTime = new DateTime();
for (int i = 0; i < ITERATIONS; i++) {
boolean test = testBigDecimal.compareTo(makeBigDecimal()) == 0;
}
endTime = new DateTime();
System.out.println("BigDecimal.compareTo()2: "
+ endTime.minus(startTime.getMillis()).toString("m:s.SSS"));
}
}
/**
* Make big decimal.
*
* @return the big decimal
*/
private BigDecimal makeBigDecimal() {
return new BigDecimal(RANDOM.nextDouble(), new MathContext(PRECISION, RoundingMode.HALF_UP))
.setScale(SCALE, RoundingMode.HALF_UP);
}
}
Output
BigDecimal.equals() : 3:42.909 BigDecimal.compareTo()1: 3:44.995 BigDecimal.compareTo()2: 3:49.476 BigDecimal.equals() : 3:48.313 BigDecimal.compareTo()1: 3:42.152 BigDecimal.compareTo()2: 3:43.889 BigDecimal.equals() : 3:42.079 BigDecimal.compareTo()1: 3:44.639 BigDecimal.compareTo()2: 3:42.564
Conclusion
When you know the scale of a BigDecimal will be consistent, equals() and compareTo() perform equally as well. In my case, I chose to stick with using equals(), as it is immediately obvious in the code what I am trying to accomplish.