首页 > 编程语言 >Java集合之一——HashMap(辨析)

Java集合之一——HashMap(辨析)

时间:2023-07-30 21:47:52浏览次数:33  
标签:index Java HashMap 辨析 equals hashcode key hash 重写

看到一篇讲hashmap的文章,讲的很不错,但是有一点我觉得作者没有讲清楚,这里我说一下自己的理解。

原文,先看原文:

https://blog.csdn.net/woshimaxiao1/article/details/83661464

前文概述,该博客的主要内容如下:

1. 什么是哈希表(主干为数组)、什么是哈希冲突、如何解决哈希冲突。这些大都是数据结构的基础知识,这里不再赘述

2.hashmap的实现原理:

简要概述一下:

主干是数组,冲突用链表解决。

每个元素其实都一个entry对象。

默认容量为16,负载因子0.75,当前元素数量大于等于容量*负载因子,扩容。

扩容后的大小为最接近当前size的二次幂。

在扩容之后,会进行元素迁移,从旧hashmap迁移到新hashmap。

为什么扩容后的大小要是二次幂?

1)在迁移的时候,会根据key重新计算hashcode,重新获取新的index。此时如果最大容量每次都是2的二次幂,那么在计算index的时候(默认情况下),有50%的概率其位置不发生改变,可以与原数组保持一致。

2)同时,如果最大长度保持为二次幂,那么散列的会更均匀,如果不是二次幂,会导致某些位置永远不会被散列到,浪费地址空间。

3.关于重写equals必须要重写hashcode的辨析。

作者通过get_entry方法引出的这个问题,但是我觉得并不很合适,原因如下:

首先我们看代码:

final Entry<K,V> getEntry(Object key) {
            
        if (size == 0) {
            return null;
        }
        //通过key的hashcode值计算hash值
        int hash = (key == null) ? 0 : hash(key);
        //indexFor (hash&length-1) 获取最终数组索引,然后遍历链表,通过equals方法比对找出对应记录
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && 
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }    

通过代码可以看到,在获取元素的时候,首先是根据key计算hashcode,然后根据hashcode找到index,之后进行两个判断

1)hashcode是否相等

2)key是否equal

作者提到,这里有些人会认为再次判断hashcode是否相等多此一举,仅通过equals即可,这是不对的。但是作者后面又说这和重写equals没重写hashcode相关,因为只重写equals不重写hashcode,那么equals可能相等,但是hashcode不等,此时按照Object的HashCode约定,不能返回元素。

我认为这里说的是对的,但是不够清晰。为了讲清楚,我们把其分为两中情况。

1)hashcode和equals都不重写,此时二者比较的都是key的地址(如果key是对象)

此时对于某一次get,可以得到一个hashcode,通过hashcode,得到index,然后进行判断

1】hashcode是否相等

2】key是否equal

此时,如果舍弃判断1】,我们会发现,因为此时equals和hashcode等价,舍弃其中任何一个都不会违背Object的HashCode约定。

2)重写equals,不重写hashcode

此时对于某一次get,可以得到一个hashcode,通过hashcode,得到index,然后进行判断

1】hashcode是否相等

2】key是否equal

此时,如果舍弃判断1】,我们会发现,因为此时equals和hashcode不等价,同时因为index是由hashcode计算出来的,不同的hashcode可能得到同样的index,没有判断1】,得到的返回值的hashcode不一定等,但是因为内容相同,可以得到返回值,此时得到的返回值是不符合Object的HashCode约定,该约定要求hashcode的范围一定要比equals大。

4.关于为什么重写equals必须要重写hashcode

如果不重写,我们会发现,在hashmap的主干(数组)上的key-value对key可能相等,但是因为hashcode不等,得到的index也不等,因为计算hashcode用的是地址,不是值。按照要求,此时应该出现hash冲突。这也导致key正确,但是定位不到正确的位置,得到正确的值,得到null。

【讲得不好,后续接着改】

5. JDK1.8中HashMap的性能优化

链表>=8,数组长>=64,链表变红黑树。

 

标签:index,Java,HashMap,辨析,equals,hashcode,key,hash,重写
From: https://www.cnblogs.com/bigcoding/p/17592089.html

相关文章

  • Java之日志
    Java之日志概述日志可以用来记录程序运行过程中的信息,并可以进行永久存储。优势可以将系统执行的信息选择性的记录到指定的位置(控制台、文件中、数据库中)可以随时以开关的形式控制是否记录日志,无需修改源代码 输出语句日志技术输出位置只能是控制台可以将......
  • java读取txt文件解决乱码问题
    说明:由于txt文件有bom和不同的编码方式,导致导入数据时产生乱码,以下代码完美解决乱码问题。参考他人代码,结合自己的业务加工完成,费了大半天功夫完成,希望对大家有点用处。废话不多说,直接上代码:/***从txt文件流读取数据**@paramtxtStream*@return......
  • Java学习6-面向对象基础 成员变量、成员方法、构造方法、this关键字、静态字段、静态
    一、面向对象概述面向过程开发,其实就是面向着具体的每一个步骤和过程,把每一个步骤和过程完成,然后由这些功能方法相互调用,完成需求。面向过程的代表语言:C语言当需求单一,或者简单时,我们一步一步去操作没问题,并且效率也挺高。可随着需求的更改,功能的增多,发现需要面对每一个步骤很麻......
  • “Java:不支持发行版本5”的解决方案
      cltr+shift+alt+s出现此页面 本地安装的jdk是8版本,所以这里显示的就是8版本,这里没有问题向下找module模块发现这里的“langeagelevel”是5将它修改成对应的版本  到File里找Settings→ Build→Compiler → javacompiler 修改成对应java版......
  • Java之异常
    Java之异常概述异常是什么?异常是代码在编译或者执行的过程中可能出现的错误异常分为几类?编译时异常、运行时异常。编译时异常:没有继承RuntimeExcpetion的异常,编译阶段就会出错。运行时异常:继承自RuntimeException的异常或其子类,编译阶段不报错,运行可能报错。学......
  • java中判断图片格式并且等比例压缩图片
    最近项目中需要判断上传的图片必须是png,jpg,gif三种格式的图片,并且当图片的宽度大于600px时,压缩图片至600px,并且等比例的压缩图片的高度。具体的实现形式:大致的思路是:判断根据文件名判断图片的格式是否是png,jpg,gif三种图片文件  定义了 方法如果是的,则进入到scaleImage(Stri......
  • 关于Java的多线程实现
    多线程介绍进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以......
  • JAVA实现海报背景填充qrCode
    packagecom.open.openbank.qrCode;importjavax.imageio.ImageIO;importjava.awt.*;importjava.awt.geom.RoundRectangle2D;importjava.awt.image.BufferedImage;importjava.io.File;importjava.io.IOException;/***生成海报*/publicclassPosterTest{......
  • java基础中(笔记)
    流程控制流程控制语句的分类:1、顺序结构:从上往下,从前往后;2、分支结构(if,switch);3、循环结构(for,while,do...while); if语句if格式:if(关系表达式){语句体;}if(关系表达式){语句体1;}else{语句体2;}if(关系表达式){语句体1;}elseif{语句体2;}elseif{语句体3;}elseif{语......
  • Java 中 == 与 equals() 的区别
    Java中==与equals()的区别1.====是一个比较运算符,在使用时有可以判断两种情况在用于基本类型时,即判断两边数据的值是否相等。在用于引用类型时,即判断两边是否为同一个对象即有相同的地址。2.equals()方法equals()方法是Object的一个方法,只能判断引用类型。O......