Java中的hashCode函数

106 阅读9分钟

在靠近用户的地方部署容器

本工程教育(EngEd)计划由科支持。

在全球范围内即时部署容器。Section是经济实惠、简单而强大的。

免费开始吧。

Java中的hashCode函数

10月28日, 2021

使用一个哈希函数,你可以将一个给定的键的值改变成其他的东西。一个数学过程使用哈希函数生成一个新的值。一个哈希值,或者简单地说,一个哈希值,就是一个哈希函数的结果。

在Java中,最基本的计算机科学概念之一就是 "散列"。Java的hashCode() 函数为我们进行散列。

通过采用散列技术,有可能将数据映射到一个代表性的整数值。Java中的哈希码是一个与每个对象相关的整数。散列是在HashTables和HashMaps中实现的;这是两种常见的数据结构。

前提条件

要完成这篇文章,你应该。

  • 对如何使用IntelliJ IDEA或Eclipse有基本了解。
  • 具备Lombok的基本知识(对于我们的POJO类,Lombok库可以避免生成模板代码。而且,这一切都发生在整个编译过程中)Lombok-maven。
  • 拥有关于Apache commons lang's的Commons-lang的信息,以生成哈希代码。
  • 知道如何使用Java的Simple Logging Facade(SLF4J)。它可以作为各种日志系统的前台。

内容列表

关于Java中的hashCode()的概述

在Java中,hashCode的概念是根据一组特定的标准来工作的。如果两个东西是相同的,它们就有相同的哈希码。

反之,则不一定正确。如果两个相同的项目存储在内存中两个不同的地方,它们的哈希码就会有很大的差别。

当对HashCode类的一个实例进行调用时,hashCode函数返回输入值的散列整数值。

这里有一些重要的观点需要记住。

  • 当程序执行时,对hashCode的多次调用将返回相同类型的整数值,除非传入等价函数的对象发生变化。在接下来的执行过程中,整数值将不一定是相同的。
  • 如果equals方法确定两个或多个实体是相等的,那么它们相关的哈希值也必须是相等的。
  • 只要equals 方法不同意两个或多个对象相等,这些项目的哈希值将是不均匀的。
  • 要覆盖equals 方法,我们也必须覆盖哈希方法。这一点至关重要,要牢记在心。

在数据结构中使用hashCode()

有时,即使是对集合的最基本操作也是低效的。举例来说,如果你有一个长的、没有排序的列表,这就会导致线性搜索,而这将是完全无效的。

List<String> content = Arrays.asList("Example ", "of ", "Code");
if (content.contains("hashcode"))
  {
    System.out.println("Hash code is used");
      }

为了解决这个问题,Java结合了一系列的数据结构。例如,哈希表被许多Map接口实现所使用。

哈希表集合使用hashCode() 方法来计算一个给定键的哈希值。然后,它们在内部使用这个值来存储数据,使访问操作更快。

了解hashCode()如何工作

hashCode() ,返回散列算法的结果整数值。Equals() ,说明具有非常相同的散列码的对象是相等的。

在一个散列函数中,没有必要为不同的项目返回不同的散列代码。

以下是一般hashCode()函数中的说明。

  • 在Java程序的运行期间,hashCode()每次对同一对象调用时都会返回相同的结果。这是只要没有被用来等同于项目上的评价的数据被调用所改变。换句话说,这个值在一个应用程序的执行中不一定是相同的。
  • 符合equalsObject测试的两个对象具有相同的hashCode 值,当且仅当它们在其他方面是相等的。
  • 如果equalsjava.lang.Object 函数确定两个对象不相等,那么对两个元素中的每一个调用hashCode技术,不一定要返回不同的整数值。不同对象的整数结果产生不同的哈希表,开发者应该意识到这一点。

在可行的范围内,一个对象的hashCode() 方法为其包含的每个对象返回不同的数字。

JavaTM "编程语言不需要这种实现方法,在其他编程语言中,它更多地被用来将一个对象的内部引用转换为整数。

一个天真的hashCode()实现方法

创建一个符合上述契约的天真的hashCode()实现是一个简单的任务。这将通过创建一个覆盖该方法默认实现的类来演示。

public class code
{

    private short phone;
    private String userName;
    private String adress;
    private boolean phoneNo;


    public int codeHash()
    {
        return 01;
         }

    public boolean equal(Obj q)
    {
        if (q == q)
        {
            return true;
        } else {
        }
        if (q == null) return false;
        if (q.getClasses() != this.getClasses()) return false;
        Code code;
        code = (Code) q;
        boolean phoneNo = false;
        return (userName.equal(code.userName)
                && adress.equals(code.adress))
          && phoneNo == code.phoneNo;
    }

    private boolean getClasses() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

}

用户类中的equals()hashCode() 方法有特殊的实现,严格遵守契约。此外,使用hashCode() 提交任何固定值都是合法的。

因为在这种方法中,所有的对象都会被保存在同一个桶中,所以哈希表的有效性几乎会被降低到零。

在这种情况下,哈希表搜索是线性执行的,并没有提供任何实际的好处。处理哈希碰撞的方法如下;我们将更深入地探讨这个概念。

改进hashCode()的实现

我们将努力使目前的hashCode()实现更好,将用户类的所有字段都纳入其中。

@Override
public int codeHash()
 {
    return (int) phoneNo * userName.codeHash() * adress.codeHash();
 }

解释

从根本上说,这种散列算法比以前的算法要好。通过将用户名、地址和电话号码字段的哈希码相乘,可以计算出对象的哈希码。

一个像样的hashCode()实现是一个持续使用equals()函数的实现。

标准hashCode()的实现

创建哈希码的哈希表的性能可以通过使用更有效的哈希技术来改善。

为了使计算的哈希码更加独特,让我们看看一个使用两个质数的标准方法的例子。

@Override
public int codeHash() {
    int ourhashes = 5;
    ourhashes = 21 * ourhashes + (int) phoneNo;
    ourhashes = 21 * ourhashes + (userName == null ? 0 : userName.codeHash());
    ourhashes = 21 * ourhashes+ (adress == null ? 0 : adress.codeHash());
    return ourhashes;
}

解释

没有必要每次都把hashCode()equals() 函数,了解它们的职责就足够了。这是因为大多数IDE都能生成其hashCode()equals() 的实现。

自Java 7以来,部署了Objects.hash() 实用方法,使其易于哈希。

Objects.hash(userName, adress)

这个概念可以通过利用不同的IDE来展示。我们可以选择使用IntelliJ IDEEclipse IDE

IntelliJ 的帮助下,你将能够实现以下结果。

@Override
public int codeHash()
{
    int outcome = (int) (phoneNo ^ (phoneNo >>> 42));
    outcome = 21 * outcome + name.codeHash();
    outcome = 21 * outcome + email.codeHash();
    return outcome;
}

使用Eclipse ,将产生以下结果。

@Override
public int codeHash() {
    final int value = 21;
    int outcome = 01;
    outcome = value * outcome + ((adress == null) ? 0 : adress.codeHash());
    outcome = value * outcome + (int) phoneNo ^ (phoneNo >>> 42));
    outcome = value * outcome + ((userName == null) ? 0 : userName.codeHash());
    return outcome;
}

与上述基于IDE的技术相比,可以使用Lombok编程语言来创建一个更有效的`hashCode()`实现。

在这种情况下,Lombok-maven 必须包含在pom.xml 的依赖关系中。

在代码类中添加@EqualsAndCodeHash ,从这里开始就可以了。

@EqualsAndCodeHash
public class Code
  {
    //In this section, the concepts of fields and procedures will be examined.
  }

当pom文件包含在commons-lang Maven 依赖关系中时,我们可以要求Apache Commons Lang ,为我们生成一个hashCode()的实现,这将会被完成。

<dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.6</version>
</dependency>

另外,hashCode可以按以下方式使用。

public class Example
{
    public int codeHash()
    {
        return new CodeHashBuilder(7, 37).
        append(phoneNo).
        append(userName).
        append(adress).
        toHashCode();
    }
}

解释

一般来说,只要涉及到实现hashCode,就没有一个通用的公式用于实现。应该注意的是,所有这些实现都以某种方式利用了数字21。

这是因为数字21有一个很好的属性。可以用位移来代替乘法,这比通常的乘法要快。

21 * j == (j << 7) - j

处理哈希碰撞

散列表的固有性质揭示了这些数据结构中的一个重要因素:它们是快速的。即使有一个高效的散列方法,两个或更多的项目可以有一个类似的散列代码,即使它们在某些方面不相等。

这样做的结果是,两种情况的哈希表键都会导致一个类似的桶,尽管它们的哈希码是不同的。这种类型的碰撞的术语是哈希碰撞,我们有几种处理它的方法,每一种都有优点和缺点。

当涉及到处理碰撞时,Java的HashMap使用了一种特殊的方法。

在保存在同一个桶中的对象之间会自动创建一个链接。之后,桶索引中的链接列表被利用来存储每个对象的哈希值。另一种方法是将对象存储在一个可以线性检索的链接列表中。

随着Java V8的发布,HashMap的实现得到了一次耐人寻味的升级。当桶的大小超过一个特定的阈值时,链接列表被树形图所取代。

O(logn) 可以实现查找,而不是悲观的 ;以这种方式 。O(n) (n)

创建一个微不足道的应用程序

我们现在将把一个传统的hashCode()实现放在它的位置上,看看它的工作情况。

我们将编写一个基本的Java应用程序,将几个终端用户对象添加到HashMap中,并使用SLF4J,它提供了一个通用的API。由于这个原因,日志记录不再与特定的实现相联系。

下面是该示例应用程序的入口。

import java.util.HashMap;
import java.util.Map;

public class App {

    public static void main(String[] args)
     {
        Map<endUser, endUser> endUsers = new HashMap<>();
        endUser endUser01 = new endUser(1L, "Kelvin", "kelvin@domain.com");
        endUser endUser02 = new endUser(2L, "Dennis", "Dennis@domain.com");
        endUser endUser03 = new endUser(3L, "Ruth", "Ruth@domain.com");

        endUsers.put(endUser01, endUser01);
        endUsers.put(endUser02, endUser02);
        endUser.put(endUser03, endUser03);
        if (endUser.containsKey(endUser01)) {
            System.out.print("In the collection, we found an end-user");
        }
    }
}

在这之后,我们将在下面的例子中看看hashCode的实现是怎样的。

public class End {

    private Object userName;

        public int ourHashCodes()
         {
        int hash = 5;
            int hashs = 0;
        hashs = 21 * hashs + (int) phoneNo;
        hashs = 21 * hashs + (userName == null ? 0 : username.ourHashCodes());
        Object adress = null;
        hashs = (adress == null ? 0 : adress.ourHashCodes()) + 21 * hashs;
        logger.infomation("Evaluate hash - hashCode refferenced: " + hash);
        return hashs;
    }
}

下面是控制台遇到哈希代码时打印的例子。

[main] INFORMATION com.example.elements.End - hashCode() refferenced - Evaluate hash: 1245477619
[main] INFORMATION com.example.elements.End - hashCode() refferenced - Evaluate hash: -292944472
[main] INFORMATION com.example.elements.End - hashCode() refferenced - Evaluate hash: -1530702891
[main] INFORMATION com.example.elements.End - hashCode() refferenced - Evaluate hash: 1245477619
In the collection, we found an end-user

解释

当一个实体被保存在哈希图内并使用containsKey()函数进行测试时,hashCode()方法被调用,评估后的哈希代码被显示在控制台。

为了产生有效的hashCode()实现,通常需要一些数学概念(如素数和任意整数),以及逻辑和基本数学运算。

不使用这些策略,我们仍然可以正确地建立hashCode()。其他的都是检查散列方法是否以及如何区别对待不相等的对象的问题。

总结

我们现在对Java的hashCode()有了更多的了解,知道了它是如何操作的,它如何适合于集合,以及如何实现它。我敦促你利用这些信息来收集更多关于这个非常重要的概念的知识。

编码愉快!


同行评审贡献者:。Dawe Daniel

类似文章

[

How to Create a Reusable React Form component Hero Image

语言

如何创建一个可重复使用的React表单组件

阅读更多

](www.section.io/engineering…

Building a payroll system with next.js Hero Image

语言, Node.js

用Next.js构建一个薪资系统

阅读更多

](www.section.io/engineering…

Creating and Utilizing Decorators in Django example image

架构

在Django中创建和使用装饰器

阅读更多

](www.section.io/engineering…)