Java hashCode()方法指南 您所在的位置:网站首页 java哈希算法 Java hashCode()方法指南

Java hashCode()方法指南

2023-09-18 19:30| 来源: 网络整理| 查看: 265

Java hashCode()方法指南

哈希是计算机学科基本概念之一。 在java中,一些常用集合都基于高效哈希算法。如HashMap、HashSet。

本文我们将重点讨论hashCode()工作原理以及如何在集合中发挥作用。

数据结构中使用hashCode()方法

一定场景下,常用集合搜索操作是非常低效的。举例,包含大量元素的list中触发线性搜索:

List words = Arrays.asList("Welcome", "to", "Baeldung"); if (words.contains("Baeldung")) { System.out.println("Baeldung is in the list"); }

java中提供一些数据结构可以解决这类问题,如几个实现Map接口的哈希实现类。

使用哈希算法的集合通过hashCode()方法计算给定键的哈希值,然后内部使用该值去存储数据,为了查询操作更有效率。

理解hashCode() 方法工作原理

简单地说,hashCode() 方法通过哈希算法生成一个整数值。 相对的对象(通过equals方法判断)返回相同哈希值,不同对象返回不同哈希值不是必须的。

hashCode()方法的算法约定为:

在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。

两个相对的对象(通过equals方法判断)必须返回相同哈希值。

两个不相对的对象(通过equals方法判断),调用hashCode()方法返回值不是必须不相等。但开发者需了解,不同对象返回不同的哈希值会提升效率。

实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)

本地hashCode() 方法实现

实际上有一个完全遵循上述契约的hashCode()方法实现非常简单。 为了演示,我们定义一个示例User类,它覆盖了该方法的默认实现:

public class User { private long id; private String name; private String email; // standard getters/setters/constructors @Override public int hashCode() { return 1; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null) return false; if (this.getClass() != o.getClass()) return false; User user = (User) o; return id != user.id && (!name.equals(user.name) && !email.equals(user.email)); } // getters and setters here }

User类提供自定义equal和hashCode方法,完全遵循各自的约定,即使hashCode方法返回任何固定值也不是非法的。

然而,这个实现完全降低哈希表的功能,每个对象将保存在相同位置,单一桶中。这时哈希表查询将执行线性搜索,完全没有带来任何优势。

改善hashCode()方法的实现

让我们稍微改善上述hashCode()方法的实现,使用User类的所有字段,为了能够让不相等的对象生成不同的结果。

@Override public int hashCode() { return (int) id * name.hashCode() * email.hashCode(); }

这个基本的哈希算法肯定要比前一个好得多,因为它只是通过将名称和电子邮件字段和id的哈希代码相乘来计算对象的哈希码。 一般来说,我们可以说这是一个合理的hashCode()实现,只要我们使equals()实现与它保持一致。

标准hashCode()实现

计算哈希值的哈希算法越好,哈希表的性能就越好。下面我们讨论标准实现,使用两个素数来增加计算哈希值的唯一性。

@Override public int hashCode() { int hash = 7; hash = 31 * hash + (int) id; hash = 31 * hash + (name == null ? 0 : name.hashCode()); hash = 31 * hash + (email == null ? 0 : email.hashCode()); return hash; }

尽管理解hashCode()和equals()方法所扮演的角色非常重要,但我们无需每次都从头实现,大多数ide都提供了生成自定义的hashCode()和equals()实现。从java 7 之后,Objects.hash() 工具方法可以方便实现:

Objects.hash(name, email)

IntelliJ IDEA 生成下面实现:

@Override public int hashCode() { int result = (int) (id ^ (id >>> 32)); result = 31 * result + name.hashCode(); result = 31 * result + email.hashCode(); return result; }

Eclipse生成实现如下:

@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((email == null) ? 0 : email.hashCode()); result = prime * result + (int) (id ^ (id >>> 32)); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; }

除了上述基于IDE的hashCode实现,也可以通过其他工具进行有效实现,如Lombok,其maven依赖如下:

org.projectlombok lombok-maven 1.16.18.0 pom

现在User类仅需要增加@EqualsAndHashCode注解就可以实现:

@EqualsAndHashCode public class User { // fields and methods here }

类似使用 Apache Commons Lang’s HashCodeBuilder 类也可以生成 hashCode() 实现, commons-lang Maven 依赖如下:

commons-lang commons-lang 2.6

hashCode()方法实现如下:

public class User { public int hashCode() { return new HashCodeBuilder(17, 37). append(id). append(name). append(email). toHashCode(); } }

一般来说,在实现hashCode()时没有通用的诀窍。强烈建议您阅读 Joshua Bloch’s Effective Java, ,它提供了实现高效散列算法的完整指南。

这里我们注意到所有这些实现都以某种形式使用数字31——这是因为31有一个很好的属性——它的乘法运算可以被比标准乘法运算快的位移所代替:

31 * i == (i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有