Let's say we have an implementation of Set<Double>
. It contains the following values: [2.0, 5.0, 7.0]
.
contains(2.0001d)
in this case returns false
because double
values are compared by exact match.
Is it possible to set some double precision for boolean contains(Object o)
method?
If it is not possible what workaround can you suggest except storing values in a sequential collection, iterating through it and comparing each value?
Set.contains
has a precise definition based upon equality:
More formally, returns
true
if and only if this set contains an element e such that(o==null ? e==null : o.equals(e))
.
It would violate the contract of the method of it used anything other than equality. And equality has a precise definition which says that it must be transitive (amongst other properties). An equality method which uses a tolerance is not transitive.
As such, there is no way for Set.contains
to allow a tolerance.
However, this is not to say that you shouldn't ever check to see if a set contains a value within a tolerance of some value - just don't try to overload the concept of contains
to do it.
For example, you could have a method which takes a NavigableSet
(for example a TreeSet
), and use its subSet
method:
static boolean containsApprox(NavigableSet<Double> set, double target, double eps) {
return !set.subSet(target - eps, true, target + eps, true).isEmpty();
}
This just requests the portion of the set which runs from target-eps
to target+eps
(inclusive, as indicated by the true
parameters). If this is non-empty, there is a value in the set within eps
of the target
.
This is clearly a separate concept from the standard Set.contains
, so it is fine for this to do a contains check which doesn't share the same properties.
You can't do the same subSet
trick with a HashMap
because it is an unordered map - there is no efficient way to extract the values in a given range. You would have to iterate the entire set, as in Sun's answer, looking for a matching value.
May be you can use anyMatch
, for example, compared based on the first two digit after .
:
Set<Double> set = Set.of(2.0, 5.0, 7.0);
Double compared = 2.0001d;
System.out.println(
set.stream().anyMatch(aDouble ->
Math.floor(aDouble * 100) / 100 == Math.floor(compared * 100) / 100
));
I can see three options:
double
wrapper class and redefine the equals methodTreeSet
with a custom comparatorNote that the last two options may or may not be satisfactory in the sense that the order in which you populate the set has an impact on which elements are retained. For example if you set the precision to 0.01 and add 0.01 then 0.011 then 0.02, you will have two elements in the set (0.01 and 0.02). If you add 0.011 then 0.01 then 0.02, you will only have one element: 0.011. I don't know if that's a problem for your use case.
The last option could look like this:
static Set<Double> setWithPrecision(double epsilon) {
return new TreeSet<> ((d1, d2) -> {
if (d1 <= d2 - epsilon) return -1;
if (d1 >= d2 + epsilon) return 1;
return 0;
});
}
Example use:
Set<Double> set = setWithPrecision(0.01);
set.add(0d);
set.add(0.00001d);
set.add(0.01d);
set.add(0.02d);
System.out.println(set); // [0.0, 0.01, 0.02]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With