The complex number is equal to the method

I am making a complex number class in Java as follows:

public class Complex { public final double real, imag; public Complex(double real, double imag) { this.real = real; this.imag = imag; } ... methods for arithmetic follow ... } 

I applied the equals method as follows:

 @Override public boolean equals(Object obj) { if (obj instanceof Complex) { Complex other = (Complex)obj; return ( this.real == other.real && this.imag == other.imag ); } return false; } 

But if you override equals, you must also override hashCode. One of the rules:

If two objects are equal according to the equals (Object) method, then calling the hashCode method for each of the two objects should give the same integer result.

Comparing float and double with == makes a numerical comparison, so the values +0.0 == -0.0 and NaN are unequal to everything, including ourselves. So I tried to implement the hashCode method to match the equals method as follows:

 @Override public int hashCode() { long real = Double.doubleToLongBits(this.real); // harmonize NaN bit patterns long imag = Double.doubleToLongBits(this.imag); if (real == 1L << 63) real = 0; // convert -0.0 to +0.0 if (imag == 1L << 63) imag = 0; long h = real ^ imag; return (int)h ^ (int)(h >>> 32); } 

But then I realized that it would work weird in a hash map if any field is NaN, because this.equals(this) will always be false, but maybe this is not true. On the other hand, I could do what double and float do, where the equals methods compare +0.0 != -0.0 , but still agree on different NaN bit patterns and allow NaN == NaN , so I get:

 @Override public boolean equals(Object obj) { if (obj instanceof Complex) { Complex other = (Complex)obj; return ( Double.doubleToLongBits(this.real) == Double.doubleToLongBits(other.real) && Double.doubleToLongBits(this.imag) == Double.doubleToLongBits(other.imag) ); } return false; } @Override public int hashCode() { long h = ( Double.doubleToLongBits(real) + Double.doubleToLongBits(imag) ); return (int)h ^ (int)(h >>> 32); } 

But if I do this, then my complex numbers do not behave like real numbers, where +0.0 == -0.0 . But I still don’t need to put my complex numbers in hash cards - I just want to do the right thing, follow the best practices, etc. And now I'm just embarrassed. Can someone advise me on the best way to continue?

+7
java equals complex-numbers
source share
3 answers

I thought about it a little more. The problem is an attempt to balance the two uses of peers: the IEEE 754 arithmetic comparison and the Object / hashtable comparison. For floating point types, two needs can never be fulfilled simultaneously due to NaN. Arithmetic comparison requires NaN != NaN , but for comparison Object / hashtable (equals method) requires this.equals(this) .

Double correctly implements methods in accordance with the Object contract, therefore NaN == NaN . It also does +0.0 != -0.0 . Both behaviors are the opposite of comparisons to primitive float / double types.

java.util.Arrays.equals(double[], double[]) compares the elements in the same way as Double ( NaN == NaN , +0.0 != -0.0 ).

java.awt.geom.Point2D This is technically wrong. His equals method only compares coordinates with == , so this.equals(this) may be false. Meanwhile, his hashCode method uses doubleToLongBits , so its hashCode may be different for two objects, even if equals returns true. Doc does not mention subtleties, which implies that the problem is not important: people do not put these types of tuples in hash tables! (And that would not be very effective if they did, because you would need to get the same numbers in order to get an equal key.)

In a floating-point tuple, similar to a complex class of numbers, the simplest correct implementation of equals and hashCode is to not override them at all. If you want the methods to take a value in the account, then the right thing to do is what Double does: use doubleToLongBits (or floatToLongBits ) in both methods. If this is not suitable for arithmetic, a separate method is needed; possibly equals(Complex other, double epsilon) for comparing numbers for equality within tolerance.

Note that you can override equals(Complex other) without interfering with equals(Object other) , but this seems too confusing.

+1
source share

The pathological case seems to be 0.0 != -0.0 , so I would make sure it doesn't happen and do the rest, exactly as Joshua Bloch says in Effective Java.

0
source share

Alternatively, the hashCode contract ensures that hashCodes are equal if the objects are equal, but not that the hash codes are different if the objects are different. That way you can just use this.real as a hash code and accept collisions. If you don’t have a priori knowledge about the distribution of numbers that your library will really encounter, you may not be able to do better: you have 128 bits of values ​​and 32 bits of a hash, so collisions are inevitable (and harmless, if only you can show that they pessimize your search for expected data sets).

0
source share

All Articles