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.
 
 
No comments:
Post a Comment