`
bound
  • 浏览: 16521 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

[学习系列]深入HashCode

阅读更多
  为什么HashCode对于对象是如此的重要?

  一个对象的HashCode就是一个简单的Hash算法的实现,虽然它和那些真正的复杂的Hash算法相比还不能叫真正的算法,它如何实现它,不仅仅是程序员的编程水平问题,而是关系到你的对象在存取是性能的非常重要的关系.有可能,不同的HashCode可能会使你的对象存取产生,成百上千倍的性能差别。

  我们先来看一下,在JAVA中两个重要的数据结构:HashMap和Hashtable,虽然它们有很大的区别,如继承关系不同,对value的约束条件(是否允许null)不同,以及线程安全性等有着特定的区别,但从实现原理上来说,它们是一致的.所以,我们只以Hashtable来说明:

  在java中,存取数据的性能,一般来说当然是首推数组,但是在数据量稍大的容器选择中,Hashtable将有比数组性能更高的查询速度.具体原因看下面的内容。

  Hashtable在存储数据时,一般先将作为key的对象的HashCode和0x7FFFFFFF做与操作,因为一个对象的HashCode可以为负数,这样操作后可以保证它为一个正整数.然后以Hashtable的长度取模,得到值对象在Hashtable中的索引。

  index = (o.hashCode() & 0x7FFFFFFF)%hs.length;这个值对象就会直接放在Hashtable的第index位置,对于写入,这和数组一样,把一个对象放在其中的第index位置,但如果是查询,经过同样的算法,Hashtable可以直接通过key得到index,从第index取得这个值对象,而数组却要做循环比较.所以对于数据量稍大时,Hashtable的查询比数据具有更高的性能。

  虽然不同对象有不同的hashcode,但不同的hashCode经过与长度的取余,就很可能产生相同的index。

  极端情况下会有大量的对象产生一个相同的索引.这就是关系Hashtable性能问题的最重要的问题:

  Hash冲突。

  常见的Hash冲突是不同key对象最终产生了相同的索引,而一种非常甚至绝对少见的Hash冲突是,如果一组对象的个数大过了int范围,而HashCode的长度只能在int范围中,所以肯定要有同一组的元素有相同的HashCode,这样无论如何他们都会有相同的索引.当然这种极端的情况是极少见的,可以暂不考虑,但是对于同的HashCode经过取模,则会产中相同的索引,或者不同的对象却具有相同的HashCode,当然具有相同的索引。

  事实上一个设计各好的HashTable,一般来说会比较平均地分布每个元素,因为Hashtable的长度总是比实际元素的个数按一定比例进行自增(装填因子一般为0.75)左右,这样大多数的索引位置只有一个对象,而很少的位置会有几个元素.所以Hashtable中的每个位置存放的是一个链表,对于只有一个对象是位置,链表只有一个首节点(Entry),Entry的next为null.然后有hashCode,key,value属性保存了该位置的对象的HashCode,key和value(对象本身),如果有相同索引的对象进来则会进入链表的下一个节点.如果同一个索引中有多个对象,根据HashCode和key可以在该链表中找到一个和查询的key相匹配的对象。

  从上面我看可以看到,对于HashMap和Hashtable的存取性能有重大影响的首先是应该使该数据结构中的元素尽量大可能具有不同的HashCode,虽然这并不能保证不同的HashCode产生不同的index,但相同的HashCode一定产生相同的index,从而影响产生Hash冲突。

  对于一个象,如果具有很多属性,把所有属性都参与散列,显然是一种笨拙的设计.因为对象的HashCode()方法几乎无所不在地被自动调用,如equals比较,如果太多的对象参与了散列.那么需要的操作常数时间将会增加很大.所以,挑选哪些属性参与散列绝对是一个编程水平的问题。

  从实现来说,一般的HashCode方法会这样:

  return Attribute1.HashCode() + Attribute1.HashCode()..[+super.HashCode()]。

  我们知道,每次调用这个方法,都要重新对方法内的参与散列的对象重新计算一次它们的HashCode的运算,如果一个对象的属性没有改变,仍然要每次都进行计算,所以如果设置一个标记来缓存当前的散列码,只要当参与散列的对象改变时才重新计算,否则调用缓存的hashCode,这可以从很大程度上提高性能。

  默认的实现是将对象内部地址转化为整数作为HashCode,这当然能保证每个对象具有不同的HasCode,因为不同的对象内部地址肯定不同(废话),但java语言并不能让程序员获取对象内部地址,所以,让每个对象产生不同的HashCode有着很多可研究的技术。

  如果从多个属性中采样出能具有平均分布的hashCode的属性,这是一个性能和多样性相矛盾的地方,如果所有属性都参与散列,当然hashCode的多样性将大大提高,但牺牲了性能,而如果只能少量的属性采样散列,极端情况会产生大量的散列冲突,如对"人"的属性中,如果用性别而不是姓名或出生日期,那将只有两个或几个可选的hashcode值,将产生一半以上的散列冲突.所以如果可能的条件下,专门产生一个序列用来生成HashCode将是一个好的选择(当然产生序列的性能要比所有属性参与散列的性能高的情况下才行,否则还不如直接用所有属性散列)。

  如何对HashCode的性能和多样性求得一个平衡,可以参考相关算法设计的书,其实并不一定要求非常的优秀,只要能尽最大可能减少散列值的聚集.重要的是我们应该记得HashCode对于我们的程序性能有着生要的影响,在程序设计时应该时时加以注意。

分享到:
评论

相关推荐

    深入Java集合学习系列:HashMap的实现原理

    在使用HashMap时,需要注意几个关键点:1) 键必须正确实现hashCode()和equals()方法,以确保哈希计算和比较的一致性;2) 避免使用null键和null值,因为HashMap的null键和null值有特殊含义;3) 考虑负载因子和初始...

    java se1 学习

    String内部存储字符串为char数组,提供了一系列便利的方法,如charAt()获取特定索引的字符,length()返回字符串长度,trim()去除两端空白,toLowerCase()和toUpperCase()转换字符串的大小写,indexOf()和lastIndexOf...

    程序员成长学习要求

    - **equals与hashCode方法**:了解这两个方法的作用及其实现方式,特别是在自定义类时如何正确重写它们,这对于保证对象一致性至关重要。 - **Synchronized与Volatile关键字**:掌握这些关键字的用法以及它们如何...

    数据结构学习

    本课程旨在帮助学习者深入理解数据结构与算法的基本概念及其应用。通过本课程的学习,学生将能够掌握各种排序算法和数据结构的代码实现方法,并学会如何分析和解决排序算法问题。此外,学生还将了解不同数据结构的...

    GoogleHashCode2021:我在2021年参加Google HashCode竞赛的尝试

    在2021年的Google HashCode竞赛中,参赛者们被邀请解决了一系列具有挑战性的编程问题,这是一场全球性的团队编程比赛,旨在促进算法、数据结构和协作技能的提升。Hash Code由Google主办,每年都会吸引众多技术爱好者...

    毕向东1406

    这个系列由知名IT教育家毕向东主讲,旨在帮助初学者深入理解Java集合框架的使用。 集合框架是Java中一个至关重要的概念,它是处理对象数组的一种高效工具,提供了存储、组织、操作数据的各种接口和类。在Java中,...

    Kotlin学习Demo

    总之,【Kotlin学习Demo】是一个宝贵的资源,它将带你深入了解Kotlin语言,并展示如何在实际项目中利用Kotlin和T-MVP架构。通过学习和实践,你将能够提升你的编程技能,为未来的开发工作打下坚实基础。

    Java常见笔试、面试系列深度剖析第六讲

    在Java编程语言中,"相等性"是一个关键的概念,特别是在面试和笔试中常常被考察。本讲将深度剖析Java中的"==运算符"和"equals()方法",这两...通过深入学习和实践,你可以更好地掌握这一核心概念,提升自己的编程能力。

    java学习路线.pdf

    本文将围绕Java学习路线,从基础到高级,详细阐述一系列关键知识点。 首先,Java的学习应从基础开始,包括了解Linux基础和Unix操作系统,因为很多服务器环境基于这些系统。理解Java底层的JVM(Java虚拟机)是必要的...

    Java核心API需要掌握的程度

    ### Java核心API需要掌握的程度 Java作为一种广泛应用的编程语言,其强大的功能很大程度上依赖于其...总之,Java核心API的学习是一个逐步深入的过程,从基本的类库到高级的应用技巧都需要不断积累经验才能真正掌握。

    java_API.rar_Javaapi _java api学习

    Java API,全称为Java ...通过深入学习和理解这些API,我们可以更高效地编写出高质量的Java应用程序。在实际开发中,应结合具体需求选择合适的API,并熟练运用其提供的方法和功能,以提高开发效率和代码质量。

    lombok-0.11.8-sources.jar.zip

    Lombok是一款非常流行的Java库,它通过提供一系列的注解,极大地简化了Java代码的编写,尤其是对于getter、setter、构造函数、equals、hashCode和toString等常见方法的生成。这个"lombok-0.11.8-sources.jar.zip...

    Java开发系列实用知识库分享

    在Java开发领域,掌握一系列实用...以上是Java开发系列中涉及到的一些核心知识点,深入理解并掌握这些内容,对于Java开发者来说是非常有益的。通过不断学习和实践,开发者可以提高自身技能,适应不断变化的开发环境。

    Java基础入门一.pdf

    Java.util.Arrays类提供了一系列用于操作数组的静态方法,如fill、set、sort、binarySearch、equals、hashCode、toString等。 扩展阅读方面,《Java编程思想》和《深入理解Java虚拟机》是值得推荐的深入学习Java和...

    手写精简版List和ArrayList,适合新手入门学习jdk源码demo

    对于新手来说,了解并动手实现这些基础功能,可以帮助深入理解`ArrayList`的工作原理,为后续的学习打下坚实的基础。 总之,通过手写`List`和`ArrayList`,我们可以更直观地感受数据结构和算法的魅力,提升编程技能...

    Java实训教程 Java软件开发实战 Java类库 第1章 Object类 共29页.pptx

    每一章都包含了丰富的示例代码和实际操作指导,确保学习者能够深入了解并熟练运用所学知识。 #### 二、第一章:Object类 **1. Object类简介** - `Object`类是Java中所有类的根类,即所有Java类都直接或间接继承自...

    java实战经典学习笔记

    ### Java实战经典学习笔记知识点概览 #### 一、Java概述及开发环境搭建 - **Java概述** - Java是一种广泛使用的高级编程语言,由Sun ...此外,通过对各种高级特性和常用类库的深入理解,可以提高开发效率和代码质量。

    Java API学习.pdf

    Java API是Java编程的...通过深入学习和理解这些类和接口,开发者可以充分利用Java平台的强大功能,创建高效、健壮的应用程序。在实际开发中,查阅JDK的帮助文档是非常有用的,它包含了Java API的详细说明和使用示例。

    Java课程大纲

    课程分为三个阶段,每个阶段都包含了一系列关键知识点。 **第一阶段:Java SE (Part 1)** 这个阶段主要是为初学者设立,帮助他们快速进入Java世界。内容包括: 1. **开发环境准备**:安装Java Development Kit ...

    Java反序列化入门之URLDNS链1

    本文将深入探讨Java反序列化的基本概念、相关方法、以及如何利用ysoserial工具进行漏洞利用,特别关注URLDNS链的原理和构建。 首先,Java反序列化是指将已序列化的对象数据恢复为原来的对象状态。在Java中,序列化...

Global site tag (gtag.js) - Google Analytics