蚀源菜单有一个“生成的hashCode / equals方法”,其产生如下面的一个功能。
String name;
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CompanyRole other = (CompanyRole) obj;
if (name == null)
{
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
如果我生成时选择多个字段hashCode()
和equals()
Eclipse使用上面示出的相同的图案。
我不是散列函数的专家,我想知道的生成散列函数如何“好”是什么? 什么是它会分解并引起太多冲突的情况?
你可以看到的hashCode函数在执行java.util.ArrayList
的
public int hashCode() {
int hashCode = 1;
Iterator<E> i = iterator();
while (i.hasNext()) {
E obj = i.next();
hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
}
return hashCode;
}
它就是这样一个例子,你的Eclipse中生成的代码如下实施它的一个类似的方式。 但是,如果你觉得你有你自己去实现你的hashCode,也有在他的名著由Joshua布洛赫给出了一些很好的指导方针有效的Java 。 我将那本书的第9项公布这些重要的观点。 那些是,
- 存储一些常非零值,说,17日,在一个int变量所谓的结果。
对于您的对象中的每个显著场f(每场考虑由equals方法,即是),请执行以下操作:
一个。 计算一个int散列码c的字段:
一世。 如果该字段是一个布尔值,计算(F?1:0)。
II。 如果该字段是一个字节,字符,短或int,计算(INT)F。
III。 如果该字段是一个长期的,计算(INT)(F ^(F >>> 32))。
IV。 如果该字段是一个浮子,计算Float.floatToIntBits(F)。
诉如果该字段是一个双面,计算Double.doubleToLongBits(f)中,然后散列长所得到的如在步骤2.a.iii。
六。 如果该字段是一个对象引用,这类的equals方法通过递归调用等于字段进行比较,递归地调用场上哈希码。 如果需要更复杂的比较,计算一个“规范表示”这个字段并且在规范表示调用哈希码。 如果该字段的值为空,则返回0(或一些其他的常数,但0是传统)
七。 如果该字段是一个数组,把它当作好像每个元素是一个独立的字段。 也就是说,通过递归地施加这些规则计算每个显著元件的哈希码,和每个步骤2.b中结合这些值 如果阵列领域的每一个元素是显著,你可以使用的1.5版增加了Arrays.hashCode方法之一。
湾 结合的散列码c计算在步骤2.a中成结果如下:
result = 31 * result + c;
返回的结果。
当你写完hashCode方法,问问自己平等的情况下,是否具有相同的哈希码。 编写单元测试,以验证自己的直觉! 如果没有相等的情况下,具有不等的散列码,找出原因并解决问题。
Java语言的设计者和Eclipse似乎遵循了类似的指南,我想。 编码愉快。 干杯。
由于Java 7,您可以使用java.util.Objects
写短而优雅的方法:
class Foo {
private String name;
private String id;
@Override
public int hashCode() {
return Objects.hash(name,id);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Foo) {
Foo right = (Foo) obj;
return Objects.equals(name,right.name) && Objects.equals(id,right.id);
}
return false;
}
}
是的,它是完美的:)你将在Java源代码几乎随处可见这种方法。
这是写散列函数的标准方式。 但是,您可以改善/简化它,如果您有任何关于领域的一些知识。 例如,你可以ommit空校验,如果类保证了现场不能为空(适用于equals()方法为好)。 或者你也可以委托的字段的哈希码如果仅使用一个字段。
I would also like to add a reference to Item 9, in Effective Java 2nd Edition by Joshua Bloch.
Here is a recipe from Item 9 : ALWAYS OVERRIDE HASHCODE WHEN YOU OVERRIDE EQUALS
- Store some constant nonzero value, say, 17, in an int variable called result.
- For each significant field f in your object (each field taken into account by the equals method, that is), do the following:
a. Compute an int hash code c for the field:
i. If the field is a boolean, compute (f ? 1 : 0).
ii. If the field is a byte, char, short, or int, compute (int) f.
iii. If the field is a long,compute(int)(f^(f>>>32)).
iv. If the field is a float, compute Float.floatToIntBits(f).
v. If the field is a double, compute Double.doubleToLongBits(f), and then hash the resulting long as in step 2.a.iii.
vi. If the field is an object reference and this class’s equals method compares the field by recursively invoking equals, recursively invoke hashCode on the field. If a more complex comparison is required, compute a “canonical representation” for this field and invoke hashCode on the canonical representation. If the value of the field is null, return 0 (or some other constant, but 0 is traditional).
vii. If the field is an array, treat it as if each element were a separate field. That is, compute a hash code for each significant element by applying these rules recursively, and combine these values per step 2.b. If every element in an array field is significant, you can use one of the Arrays.hashCode methods added in release 1.5.
b. Combine the hash code c computed in step 2.a into result as follows: result = 31 * result + c;
3. Return result.
4. When you are finished writing the hashCode method, ask yourself whether equal instances have equal hash codes. Write unit tests to verify your intuition! If equal instances have unequal hash codes, figure out why and fix the problem.
如果您使用的是Apache软件基金会(公地郎库)然后在下面的类会帮助你使用反射来生成散列码/等于/的toString方法。
当您添加/删除实例变量您不必担心再生哈希代码/等于/的toString方法。
EqualsBuilder -这个类提供的方法来建立一个良好的equals方法对任何类。 它遵循有效的Java摆出来,由Joshua布洛赫规则。 特别是对于比较双打,治浮,并且阵列可能会非常棘手。 此外,确保equals()和hashCode()方法是一致是很困难的。
HashCodeBuilder -这个类允许任何类中树立了良好的hashCode方法。 它遵循的书有效的Java由约书亚布洛赫所设定的规则。 编写一个好的hashCode方法实际上是相当困难的。 这个类的目的是简化流程。
ReflectionToStringBuilder -这个类使用反射来确定字段追加。 因为这些字段通常私有的,该类使用AccessibleObject.setAccessible(java.lang.reflect.AccessibleObject中[],布尔值)更改的字段的可见性。 这将安全管理器下可能会失败,除非相应的权限设置是否正确。
Maven的相关性:
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons.lang.version}</version>
</dependency>
示例代码:
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
public class Test{
instance variables...
....
getter/setter methods...
....
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
}
一个潜在的缺点是,随着空字段的所有对象都会有31哈希代码,从而有可能是只包含空字段的对象之间存在许多潜在的冲突。 这将使在较慢的查找Maps
。
当你有一个可能出现这种情况Map
其主要类型有多个子类。 举例来说,如果你有一个HashMap<Object, Object>
,你可以有它的散列码是31诚然,这不会经常发生,许多键值。 如果你喜欢,你可以随意改变黄金的价值观的东西,除了31,并减少冲突的可能性。