Java Faster Equals方法适用于float类型

时间:2016-07-17 09:04:58

标签: java floating-point set equals

据我所知set.add使用FastFloat的equals方法

对我来说重要的只是前点后两位数(!!!),所以在equals方法中使得等于更快我使用Math.abs() >= 0.001但我不明白为什么这段代码会返回2而不是1因为Math.abs(3.54 - 3.5405) < 0.001 代码:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class st {

public class FastFloat {
    private float ff;


    public FastFloat(float ff) {
        super();
        this.ff = ff;
    }


    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + getOuterType().hashCode();
        result = prime * result + Float.floatToIntBits(ff);
        return result;
    }


    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof FastFloat))
            return false;
        FastFloat other = (FastFloat) obj;
        if (!getOuterType().equals(other.getOuterType()))
            return false;
/*          if (Float.floatToIntBits(ff) != Float.floatToIntBits(other.ff))
            return false;*/
        if (Math.abs(ff - other.ff) >= 0.001)
            return false;
        return true;
    }


    private st getOuterType() {
        return st.this;
    }


}
public static void main(String[] args) {
    List<Set<FastFloat>> lsff = new ArrayList<>();
    lsff.add(0, new HashSet<>());
    Set<FastFloat> sff = lsff.get(0);
    st x = new st();
    sff.add(x.new FastFloat((float)3.54));
    sff.add(x.new FastFloat((float)3.5405));
    System.out.println(lsff.get(0).size());
}

}

3 个答案:

答案 0 :(得分:3)

您的hashCode()方法为ff==3.54ff==3.5405提供了不同的结果,因此它们被分配到HashSet的不同存储区,而您的equals方法永远不会用于测试它们是否相等。

如果a.equals(b)a.hashCode()必须等于b.hashCode()。这是合同。

来自equals() Javadoc:

  

请注意,通常需要在重写此方法时覆盖hashCode方法,以便维护hashCode方法的常规协定,该方法声明相等对象必须具有相等的哈希码

答案 1 :(得分:1)

您实施的几乎等于的类型不符合Object's contract for equals,因为它不具有传递性。

考虑0,0.999和0.0018的情况。 Math.abs(0.0009 - 0)Math.abs(0.0018 - 0.009)一样小于0.001,但Math.abs(0.0018 - 0)大于0.001。

有一个几乎等于的方法,并在某些情况下使用它,但要使HashSet和其他哈希结构有效,你需要equalshashCode符合Object合同。

如果唯一的原因是&#34;速度&#34;,摆脱并发症并使用Float

如果您还有其他原因需要在HashSet中将不相等的浮点数组合在一起,有很多方法可以将它们划分为等价类,包括k5_'s answer中讨论的等价类。您还可以将其基于纯浮点运算,而无需提取和操作位模式。例如,将其基于除以0.001:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class st {

  public class FastFloat {
    private float ff;
    private float fudgeFactor = 0.001f;
    private float fudged;

    public FastFloat(float ff) {
      super();
      this.ff = ff;
      this.fudged = (float) Math.rint(ff / fudgeFactor);
    }

    @Override
    public int hashCode() {
      return Float.hashCode(fudged);
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (!(obj instanceof FastFloat))
        return false;
      FastFloat other = (FastFloat) obj;
      return Float.compare(this.fudged, other.fudged) == 0;
    }

    private st getOuterType() {
      return st.this;
    }

  }

  public static void main(String[] args) {
    List<Set<FastFloat>> lsff = new ArrayList<>();
    lsff.add(0, new HashSet<>());
    Set<FastFloat> sff = lsff.get(0);
    st x = new st();
    sff.add(x.new FastFloat((float) 3.54));
    sff.add(x.new FastFloat((float) 3.5405));
    System.out.println(lsff.get(0).size());
    sff.add(x.new FastFloat(0f));
    System.out.println(lsff.get(0).size());
  }

}

答案 2 :(得分:0)

如果你想为浮点数实现.equals() / .hashCode(),一些浮点数是相等的。因此,您可以将自己的班级用于HashSetHashMap

这样做的一种方法是从Float.floatToInBits()去除最后的&lt; 23位(当您删除不是十进制的二进制位置,以保留4个有效小数位,大约删除10个二进制位))。您可以在.equals().hashCode()实施中使用生成的int。

我没有用二进制进行数学运算,因此数字实际上并不正确(如果以十进制表示,则为例子)

  • 像1000000这样的东西将等于1000555,但会与999999
  • 不同
  • 像1,00101这样的东西将等于1,0013但不同于1,00099

此方法会产生有效的.equals() / .hashCode()但不提供Math.abs()&lt;你想要的是0.001(在相同的情况下,abs会更高(!)更高,在某些情况下更低)。如果该属性对您更重要,请不要使用.equals() / .hashCode()并创建自己的集合。