9

Initially code was :

            if(!Objects.equals(src.getApplicationItemCost(), dest.getApplicationItemCost())){
               log.info("Difference")
            }

Output:

      getApplicationItemCost: src:0.0| dest:0
      Difference

ApplicationItemCost is of type BigDecimal.

If I use compareTo then I have to explicitly check nulls like :

LOG.info("getApplicationItemCost: src:" + src.getApplicationItemCost() + "| dest:" + dest.getApplicationItemCost());
        if((src.getApplicationItemCost()==null && dest.getApplicationItemCost()!=null)
                || (src.getApplicationItemCost()!=null && dest.getApplicationItemCost()==null)
                || !Objects.equals(src.getApplicationItemCost(), dest.getApplicationItemCost())
                || src.getApplicationItemCost().compareTo(dest.getApplicationItemCost())!=0 )

Any suggestion to compare 0.0 and 0. Why is this difference? (May be database has Number field and when converted to big decimal it does not show 0.0?)

fatherazrael
  • 5,511
  • 16
  • 71
  • 155
  • Does this answer your question? [How to check if BigDecimal variable == 0 in java?](https://stackoverflow.com/questions/10950914/how-to-check-if-bigdecimal-variable-0-in-java) – Sharon Ben Asher Feb 12 '20 at 06:54
  • what's the return types of getApplicationItemCost() method? – Md Golam Rahman Tushar Feb 12 '20 at 06:55
  • @SharonBenAsher: Yes but for compareTo i need to have null checks explicitly. I am trying to avoid that. So any method or any utility. – fatherazrael Feb 12 '20 at 07:04
  • 1
    @fatherazrael you're trying to avoid null checks? But you *need* null checks, if they might be null. – Andy Turner Feb 12 '20 at 07:22
  • 1
    Why are 0.0 and 0 different? Look at the source and you'll see what's compared are `intVal` and `scale`. Both have the same `intVal` but a different `scale`, hence the difference in output (with and without a decimal point). – Thomas Feb 12 '20 at 07:32

3 Answers3

16

Build a comparator:

Comparator<BigDecimal> c = Comparator.nullsFirst(Comparator.naturalOrder());

(Or nullsLast, it doesn't matter if you're only ever comparing to zero).

Then:

if (c.compare(first, second) != 0) {
  // ...
}
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • 2
    That answers the question literally, but it’s worth noting that the prerequisites are flawed. Since the example hints that the values originate from a method like `getApplicationItemCost()`, in other words, represent monetary values, they should have the same scale in the first place or just be `BigInteger` instead of `BigDecimial` and have the scale implied. And they should never be `null` (are `null` costs different to zero costs?)… – Holger Feb 12 '20 at 09:37
  • Yes. The values contains 20.0 , 800.0 and should not be null functionally. But in first data load somehow null values got into the fields. It should be handled in Database but as of now no control of production servers. Just in test. In my changes i even included new BigDecimal(0.0) in Entity. – fatherazrael Feb 12 '20 at 11:11
2

Try using

CompareToBuilder

class provided by

org.apache.commons.lang3

as it make null safe comparison. Sample code is:

public static <T, U> int nullSafeComparison(T t, U u) {
        return new CompareToBuilder().append(t, u).toComparison();
    }

public static void main(String[] args) {
        BigDecimal zero = BigDecimal.ZERO;
        BigDecimal zeroPzero = new BigDecimal("0.0");

        System.out.println( zero + " " + zeroPzero);

        System.out.println(nullSafeComparison(zero, zeroPzero));
    }

If both numbers are same it will return 0, if 1st is greater than 2nd the result will be 1 and -1 if 1st number is less than 2nd.

oOXAam
  • 237
  • 1
  • 6
  • 20
  • @oOXAm: So if any of them are null then it will not throw any exception. Cool. Thanks. let me try – fatherazrael Feb 12 '20 at 07:06
  • yeah, no exception. It will return 0 if both are null. – oOXAam Feb 12 '20 at 07:15
  • 1
    That looks like magic that is prone to fail at runtime. You declare two different type variables, `T` and `U` which do not extend `Comparable` and have no relationship at all. In other words, these type variables do not add anything to the code, it’s just like both parameters were declared as `Object`. E.g. `nullSafeComparison(new Button(), new Thread())` passes without any compiler errors. – Holger Feb 12 '20 at 09:50
  • You're right, but the whole point of the introduction of Generic is if you want to compare Integer, Long Bigdecimal, etc, you won't have to make separate methods for each. So by doing this you can use it for multiple data types. – oOXAam Feb 12 '20 at 10:42
  • 3
    Seems you don’t understand how to use Generics correctly. As said, the way you’ve declared the method, it’s not different to `nullSafeComparison(Object t, Object u)`, you can just pass in everything. To ensure that the arguments are compatible, there should be *one* type variable, to be used for both parameters, and it should have an appropriate bound. See [this method](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Collections.html#sort(java.util.List)) for an example of how to declare that the type argument must have a natural order. – Holger Feb 12 '20 at 15:50
1

Custom BigDecimal null safe Comparison (without any exteranal library):

{
    BigDecimal big1, big2; 

    big1 = new BigDecimal(0); 
    big2 = new BigDecimal(0.0); 

    int compareResult1 = compareTo(b1,null);
    int compareResult2 = compareTo(null,b2);
}

public static <T extends Comparable<T>> int compareTo(final T c1, final T c2) {
    final boolean f1, f2;
    return (f1 = c1 == null) ^ (f2 = c2 == null) ? f1 ? -1 : 1 : f1 && f2 ? 0 : c1.compareTo(c2);
}
Suryakant Bharti
  • 673
  • 1
  • 6
  • 24
  • this is not null safe – Sharon Ben Asher Feb 12 '20 at 07:12
  • This is quite complex code for a simple comparison that can be made easier and without using any external library (see Andy's answer for one such example). – Thomas Feb 12 '20 at 07:35
  • I removed the type parameter from your `bigDecimalCompareTo` method; but what's the point in `bigDecimalCompareTo`, when you can just invoke `compareTo`? – Andy Turner Feb 12 '20 at 07:48
  • 2
    Quiet complicated with assignments within comparison expressions and using the `^` operator (which is not different to `!=` for `boolean` values). You can use a simple, straight-forward statement without additional variables like `return c1 == c2? 0: c1 == null? -1: c2 == null? 1: c1.compareTo(c2);` – Holger Feb 12 '20 at 09:44