Java Top.

使用Spring 5和Spring Boot 2开始,通过学习春天课程:

>>看看这个课程

1.概述

哈希是计算机科学的基本概念。

在Java中,高效的散列算法站在一些最受欢迎的集合之后,如Hashmap.(检查这一深度文章)和hashset.

在本教程中,我们将专注于如何hashcode()作品,如何扮演集合以及如何正确实施。

进一步阅读:

Java Equals()和hashcode()合同

了解等于()金宝搏官网188be和hascode()需要满足的合同以及两种方法之间的关系

使用Eclipse生成Equals()和HashCode()

使用Eclipse IDE生成Equals()和Hashcode()的快速实用指南

项目龙目岛介绍

这是一个关于标准Java代码的Project Lombok的许多有用用例的全面和非常实用的介绍。

2.使用hashcode()在数据结构中

在某些情况下,集合上最简单的操作可能效率低。

为了说明,这触发了线性搜索,这对于庞大的列表非常无效:

list  words = arrays.aslist(“欢迎”,“to”,“baeldu金宝搏188体育ng”);if(lock.Contains(“ba金宝搏188体育eldung”)){system.out.println(“baeldung在列表中”);}

Java提供了许多数据结构,用于具体处理此问题。例如,几个地图界面实现是哈希表

当使用哈希表时,类计算给定键的散列值hashcode()方法。然后,他们在内部使用此值来存储数据,以便访问操作更高效。

3.了解hashcode()作品

简单的说,hashcode()返回由散列算法生成的整数值。

等于的对象(根据他们的equals ())必须返回相同的散列码。不同的对象不需要返回不同的哈希代码。

一般合同hashcode()状态:

  • 每当在执行Java应用程序期间在同一个对象上稍微超过一次,hashcode()必须一致地返回相同的值,不提供在对象上的等于比较中使用的信息没有。此值不需要从应用程序的一次执行到另一个执行相同应用程序的执行。
  • 如果两个物体相等等于(对象)方法,致电hashcode()两个对象中的每一个必须产生相同的值。
  • 如果两个物体不相等,根据等于(java.lang.Object)方法,致电哈希科特两个对象中的每一个的方法不需要产生不同的整数结果。但是,开发人员应该意识到为不平等对象产生不同的整数结果,提高了哈希表的性能。

“尽可能实用,是hashcode()由类定义的方法目的为不同的对象返回不同的整数。(这通常通过将对象的内部地址转换为整数来实现,但是Javatm编程语言不需要此实现技术。)“

一个天真的hashcode()执行

一个天真的hashcode()完全遵守上述约定的实现实际上是相当简单的。

为了证明这一点,我们将定义一个样本用户覆盖方法默认实现的类:

公共类用户{私人长ID;私有字符串名称;私有字符串电子邮件;//标准getters / setter / setders @override public int hashcode(){返回1;@Override公共布尔等于(对象O){如果(这== o)返回true;if(o == null)返回false;if(thet.getClass()!= o.getClass())返回false;用户user =(用户)o;return id == user.id &&(name.equals(user.name)&& mode.equals(user.email));} // getters和setter在这里}

用户类为两者提供自定义实现equals ()hashcode()完全遵守相应的合同。甚至更多,没有什么是非法的hashcode()返回任何固定值。

但是,此实现将哈希表的功能降低到基本为零,因为每个对象都将存储在相同的单桶中。

在此上下文中,哈希表查找线性地执行,并且不会给我们任何真正的优势。我们在第7节中讨论了这一点金宝搏官网188be。

5.改善hashcode()执行

让我们改善电流hashcode()通过包括所有领域的实施用户班级使其可以为不平等对象产生不同的结果:

@override public int hashcode(){return(int)id * name.hashcode()*电子邮件.hashcode();}

这种基本的散列算法明确地比前一个更好。这是因为它通过乘以散列代码来计算对象的哈希码名称电子邮件田野和田野ID

一般而言,我们可以说这是一个合理的hashcode()实施,只要我们保留equals ()实施一致。

6.标准hashcode()实现

我们用于计算哈希代码的散列算法越好,哈希表的性能越好。

让我们来看看一个使用两个素数的“标准”实现来为计算的哈希代码添加更多唯一性:

@Override public int hashCode() {int哈希= 7;哈希= 31 *哈希+ (int) id;Hash = 31 * Hash + (name == null ?)0: name.hashCode ());Hash = 31 * Hash + (email == null ?0: email.hashCode ());返回哈希;}

虽然我们需要了解这个角色hashcode()equals ()方法播放,我们每次都不必从头开始实施它们。这是因为大多数IDE可以生成自定义hashcode()equals ()实施。自从Java 7以来,我们有一个Objects.hash ()舒适散列的实用方法:

Objects.Hash(姓名,电子邮件)

intellij想法生成以下实现:

@override public int hashcode(){int结果=(int)(id ^(id >>> 32));结果= 31 *结果+ name.hashcode();结果= 31 *结果+ Email.HashCode();返回结果;}

产生这个:

@override public int hashcode(){final int prime = 31;结果= 1;结果= Prime *结果+((电子邮件== null)?0:Email.HashCode());结果= Prime *结果+(int)(ID ^(id >>> 32));结果= Prime *结果+((name == null)?0:name.hashcode());返回结果;}

除了上述基于IDEhashcode()实现,也可以自动生成有效的实现,例如使用Lombok

在本例中,我们需要添加lombok-maven依赖,pom.xml.

<依赖项>  org.projectLombok  龙目像-MAVEN   1.16.18.0   pom  

它现在足以注释用户班级@equalsandhashcode.

@EqualsAndHashCode公共类用户{//字段和方法在这里}

同样,如果我们想要Apache Commons Lang的HashCodeBuilder班级生成A.hashcode()为我们实施,我们包括Commons-Lang.pom文件中的Maven依赖项:

<依赖>  commons-lang   Commons-Lang   2.6  

hashcode()可以这样实现:

public class User {public int hashCode() {return new HashCodeBuilder(17,37)。附加(id)。追加(名字)。追加(电子邮件)。toHashCode ();}}

一般来说,在实施时没有普遍的配方hashcode()。我们强烈推荐阅读Joshua Bloch的Effective Java。它提供了一个列表彻底指导实现高效散列算法。

请注意,所有这些实现都以某种形式利用数字31。这是因为31有一个很好的财产。它的乘法可以通过按位偏移替换,这比标准乘法更快:

31 * i ==(我<< 5) - 我

7.处理哈希碰撞

哈希表的内在行为带来了这些数据结构的一个相关方面:即使使用了有效的哈希算法,两个或多个对象也可能具有相同的哈希代码,即使它们不相等。因此,它们的哈希代码将指向同一个桶,即使它们有不同的哈希表键。

这种情况通常称为哈希碰撞,有各种方法可以处理它,每个人都有他们的利弊。javaHashmap.使用分离链接法处理碰撞:

“当两个或多个对象指向同一桶时,它们只是存储在链接列表中。在这种情况下,哈希表是链接列表的数组,并且将具有相同哈希的每个对象附加到阵列中的桶索引处的链接列表。

在最坏的情况下,几个bucket将绑定一个链表,并且在链表中检索一个对象将线性执行。”

哈希碰撞方法以简称为何显示为什么它实现这么重要hashcode()有效率的

Java 8带来了一个有趣的增强Hashmap.实现。如果桶的大小超过了某个阈值,则树映射将替换链表。这样就可以实现O (logn查找而不是悲观上)

8.创建琐碎的应用程序

现在我们将测试标准的功能hashcode()执行。

让我们创建一个添加一些的简单Java应用程序用户对象到A.Hashmap.并使用SLF4J.每次调用该方法时将消息记录到控制台。

以下是示例应用程序的入口点:

公共类应用程序{公共静态void main(String [] args){map 用户= new hashmap <>();用户user1 =新用户(1L,“John”,“Joh[电子邮件受保护]”);用户user2 = new User(2L, "Jennifer", "Jennifer")[电子邮件受保护]“);用户user3 =新用户(3L,”Mary“,”[电子邮件受保护]“); Users.put(User1,User1); Users.put(User2,User2); Users.put(User3,User3); if(Users.ContainsKey(User1)){system.out.print(”找到用户收集“);}}}

这是hashcode()执行:

public class User{//…public int hashCode() {int哈希= 7;哈希= 31 *哈希+ (int) id;Hash = 31 * Hash + (name == null ?)0: name.hashCode ());Hash = 31 * Hash + (email == null ?0: email.hashCode ());logger.info("hashCode()调用-计算散列:" +散列);返回哈希;}}

这里,重要的是要注意,每次一个对象存储在哈希映射中并使用该对象containsKey ()方法,hashcode()调用,并将计算出的哈希码打印到控制台:

[main] info com.金宝搏188体育baeldung.entities.user  -  hashcode()调用 -  computed hash:1255477819 [main] info com.baeldung.entities.user  -  hashcode()调用 -  computed hash:-282948472 [main]信息com.baeldung.entities.user  -  hashcode()调用 -  computed hash:-1540702691 [main] info com.bae金宝搏188体育ldung.entities.user  -  hashcode()调用 -  computed hash:1255477819在集合中找到的用户

9.结论

很明显,生产高效hashcode()实现通常需要一些数学概念(即素数和任意数)的混合,逻辑和基本的数学运算。

无论如何,我们可以实施hashcode()实际上没有诉诸这些技术。我们只需要确保散列算法为不等象产生不同的哈希代码,并且它与实现一致equals ()

一如既往地,本文中显示的所有代码示例都可提供在github上

Java底部

使用Spring 5和Spring Boot 2开始,通过学习春天课程:

>>看看这个课程
4.评论
最老的
最新
内联反馈
查看所有评论
评论在本文上关闭!