0

I hava a Map<Integer,Double> as a field. I need to implement equals() for that given class.

How to compare the double values using a tolerance.

LonsomeHell
  • 573
  • 1
  • 7
  • 29
  • 2
    you can always take the difference between the two and check if it is larger than the tolerance – Bishakh Ghosh May 24 '17 at 09:27
  • 1
    What are you comparing? The keys? The values? Every entry ? – Turtle May 24 '17 at 09:29
  • 5
    You *don't* implement `equals()` with a tolerance. Use another method name. Otherwise you will run into trouble - starting with breaking the [contract between `hashCode()` and `equals()`](https://stackoverflow.com/questions/27581/what-issues-should-be-considered-when-overriding-equals-and-hashcode-in-java). – Axel May 24 '17 at 09:30
  • Key type here is `Integer` then what is need to compare double in `equals()` method? – Sanket Makani May 24 '17 at 09:31
  • I need to compare the whole map keys and values – LonsomeHell May 24 '17 at 12:47

1 Answers1

1
public class Foo {
Map<Integer, Double> data;

public Map<Integer, Double> getData() {
    return data;
}

public void setData(Map<Integer, Double> data) {
    this.data = data;
}

@Override
public boolean equals(Object o) {
    if (this == o)
        return true;
    if (!(o instanceof Foo))
        return false;
    Foo foo = (Foo) o;
    if (this.data.size() != foo.getData().size())
        return false;

    Set<Integer> keySet1 = data.keySet();
    Set<Integer> keySet2 = foo.getData().keySet();
    // keys should same
    if (keySet1.containsAll(keySet2) && keySet2.containsAll(keySet1)) {
        // for the same key, the values are close
        for (Integer key : keySet1) {
            if (!isEntryEqual(data.get(key), foo.getData().get(key))) {
                return false;
            }
        }
        return true;
    }


    return false;
}

// also need to override the hashCode method


@Override
public int hashCode() {
    List<Integer> keys = new ArrayList<Integer>(this.data.keySet());
    return Objects.hash(keys);
}

public static final Double PRECISION = 0.0001;

private static boolean isEntryEqual(Double d1, Double d2) {
    return d1 - d2 < PRECISION;
}
}
vonzhou
  • 339
  • 1
  • 5
  • A matching implementatioon of `hashCode()` is missing. – Axel May 24 '17 at 10:57
  • I think hashCode() can be shortened to `return this.data.keySet().hashCode()`. – Axel May 24 '17 at 14:31
  • @LonsomeHell Please read [Transitive nature of equals method](https://stackoverflow.com/questions/7697213/transitive-nature-of-equals-method) to understand why this is a bad idea and could lead to subtle bugs, – Axel May 24 '17 at 14:34
  • @Axel How would this lead to a bug. I know that handling floating point number is tricky. – LonsomeHell May 24 '17 at 14:51
  • You add three instances of your class to a set. Depending on the order you add them, the set contains one or two entries. I think this could easily lead to a bug. And since you don't want to implement your comparison in some other method, `isNearlyEquals()` or so, it looks like you are about to put instances of your class into a standard container. It might as well work in the particular use case you have, but better not make this a habit. – Axel May 24 '17 at 14:58
  • Math.abs(d1-d2) <= PRECISION would be the correct logic for isEntryEqual – Thar Apr 25 '23 at 10:35