java.awt.geom.AffineTransform.hashCode() is inconsistent with equals(Object)

Martin Desruisseaux martin.desruisseaux at geomatys.fr
Fri May 20 15:56:43 UTC 2011


Hello all

I realize that this is a Java2D bug, but given that it is a simple 
equals(Object)/hashCode() implementation issue maybe this list is still relevant...

AffineTransform.hashCode() method is inconsistent with equals(Object) when some 
'double' values are 0.0 values of opposite sign. The reason is that 'hashCode()' 
uses 'doubleToLongBits(double)' which return different values for positive and 
negative zero, while 'equals(Object)' uses '==' which treat -0.0 as equals to 
+0.0. I verified in latest JDK 7 source code that this bug is still present.

Trivial test case:

import java.awt.geom.AffineTransform;
public class AffineBug {
     public static void main(String[] args) {
         AffineTransform tr1 = new AffineTransform();
         AffineTransform tr2 = new AffineTransform();
         tr2.translate(-0.0, 0);
         System.out.println("hashCode 1: " + tr1.hashCode());
         System.out.println("hashCode 2: " + tr2.hashCode());
         System.out.println("equals: " + tr1.equals(tr2));
     }
}


Current AffineTransform.equals(Object) implementation is:

     public boolean equals(Object obj) {
         if (!(obj instanceof AffineTransform)) {
             return false;
         }
         AffineTransform a = (AffineTransform)obj;
         return ((m00 == a.m00) && (m01 == a.m01) && (m02 == a.m02) &&
                 (m10 == a.m10) && (m11 == a.m11) && (m12 == a.m12));
     }

Possible fix would be (assuming appropriate static import):

     public boolean equals(Object obj) {
         if (!(obj instanceof AffineTransform)) {
             return false;
         }
         AffineTransform a = (AffineTransform)obj;
         return ((doubleToLongBits(m00) == doubleToLongBits(a.m00))
&& (doubleToLongBits(m01) == doubleToLongBits(a.m01))
&& (doubleToLongBits(m02) == doubleToLongBits(a.m02))
&& (doubleToLongBits(m10) == doubleToLongBits(a.m10))
&& (doubleToLongBits(m11) == doubleToLongBits(a.m11))
&& (doubleToLongBits(m12) == doubleToLongBits(a.m12)));
     }


Note that this have the side effect of considering NaN values as equal.

Similar bug affect also Point2D and Rectangle2D (I did not checked the other 
geometric classes).

     Regards,

         Martin




More information about the core-libs-dev mailing list