HashMap如何与冲突作斗争

2023-12-23 14:20
文章标签 hashmap 冲突 斗争

本文主要是介绍HashMap如何与冲突作斗争,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 哈希表
      • 1.1概念
      • 1.2哈希冲突
        • 1.21冲突-避免-负载因子调节(重点)
      • 1.22哈希冲突-解决
        • 1.23 冲突-解决-闭散列
        • 1.23 冲突-解决-开散列/哈希桶(重点)
      • 1.3一些有助于了解学习HashMap的文章

哈希表

1.1概念

顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O( log2N),搜索的效率取决于搜索过程中元素的比较次数。
理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。 如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。
当向该结构中:

  • 插入元素
    根据待插入元素的关键码,以此函数计算出存储位置并存放至计算出的位置
  • 搜索元素
    对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功
    该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(HashTable)(散列表)。

1.2哈希冲突

在这里插入图片描述首先,我们需要明确一点,由于我们哈希表底层数组的容量往往是小于实际要存储的关键字的数量的,这就导致一个问题,冲突的发生是必然的,但我们能做的应该是尽量的降低冲突率
引起哈希冲突的一个可能原因就是哈希函数设计不合理,哈希函数有其自己的设计原则,感兴趣的同学可以自己去了解一下。

1.21冲突-避免-负载因子调节(重点)

在这里插入图片描述在这里插入图片描述哈希表中已有关键字的个数是不可以改变的而降低负载因子其实就是增加哈希表中数组长度的大小(resize)
HashMap底层负载因子默认大小:
在这里插入图片描述

  • 什么时候需要扩容?
    我们在调用HashMap的put方法时,如果当前的数组(HashMap的底层数据结构就是数组)为null,或者数组的长度大于阈值(数组长度*负载因子)时,会发生扩容。数组为null时,会扩容成默认长度或指定长度;数组超过阈值时,会扩容成原数组的两倍。

为什么是负载因子0.75呢?可以看上面负载因子是如何计算的,我认为这是一种折中的思想,如果负载因子太小,就会造成空间的浪费,太大那么冲突的概率就会更大。(这是我自己认为的,具体原因请大家子行了解)。
数组一旦初始化后大小就无法改变了,所以就有了 ArrayList这种“动态数组”,可以自动扩容。
在Java中,数组是无法自动扩容的,所以如果要扩容的话,就需要新建一个大的数组,然后把小数组的元素复制过去。

-resize方法的源码

//jdk1.7 resize源码
// newCapacity为新的容量
void resize(int newCapacity) {// 小数组,临时过度下Entry[] oldTable = table;// 扩容前的容量int oldCapacity = oldTable.length;// MAXIMUM_CAPACITY 为最大容量,2 的 30 次方 = 1<<30if (oldCapacity == MAXIMUM_CAPACITY) {// 容量调整为 Integer 的最大值 0x7fffffff(十六进制)=2 的 31 次方-1threshold = Integer.MAX_VALUE;return;}// 初始化一个新的数组(大容量)Entry[] newTable = new Entry[newCapacity];// 把小数组的元素转移到大数组中transfer(newTable, initHashSeedAsNeeded(newCapacity));// 引用新的大数组table = newTable;// 重新计算阈值threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}

关于扩容后为什么容量要保持2的幂次方,这也是一个面试经常会被问的问题,这个问题大家自行了解。网上有很多,讲的都很好,在此不赘述

1.22哈希冲突-解决

解决哈希冲突两种常见方法是:闭散列和开散列

1.23 冲突-解决-闭散列

也叫开放地址法当发生哈希冲突时,如果哈希表未被装满,那说明哈希表中必然有空位置,于是我们就可以把key存放到发生冲突位置的“下一个”空位置中,如何寻找下一个位置?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

1.23 冲突-解决-开散列/哈希桶(重点)

开散列法又叫链地址法,首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。单链表中插入是采用尾插法(jdk1.8开始)。链表长度超过8,这个链表会变为红黑树。
在这里插入图片描述从上图可以看出,开散列中每个桶中放的都是发生哈希冲突的元素。
开散列,可以认为是把一个在大集合中的搜索问题转化为在小集合中做搜索了。
具体细节可参考此文:
hashmap如何解决哈希冲突

  1. HashMap 和 HashSet 即 java 中利用哈希表实现的 Map 和 Set
  2. java 中使用的是哈希桶方式解决冲突的
  3. java 会在冲突链表长度大于一定阈值后,将链表转变为搜索树(红黑树)
  4. java 中计算哈希值实际上是调用的类的 hashCode 方法**,进行 key 的相等性比较是调用 key 的 equals 方法。所以如果要用自定义类作为 HashMap 的 key 或者 HashSet 的值,必须覆写 hashCode 和 equals 方 法,而且要做到 equals 相等的对象,hashCode 一定是一致的。**

1.3一些有助于了解学习HashMap的文章

详细的HashMap源码解析

HashMap的底层实现原理

这篇关于HashMap如何与冲突作斗争的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/528342

相关文章

六、Maven依赖管理、依赖传递和依赖冲突

1.Maven依赖管理 Maven 依赖管理是 Maven 软件中最重要的功能之一。Maven 的依赖管理能够帮助开发人员自动解决软件包依赖问题,使得开发人员能够轻松地将其他开发人员开发的模块或第三方框架集成到自己的应用程序或模块中,避免出现版本冲突和依赖缺失等问题。 我们通过定义 POM 文件,Maven 能够自动解析项目的依赖关系,并通过 Maven 仓库自动下载和管理依赖,从而避免了手动

[数据结构] 哈希结构的哈希冲突解决哈希冲突

标题:[C++] 哈希结构的哈希冲突 && 解决哈希冲突 @水墨不写bug 目录 一、引言         1.哈希         2.哈希冲突         3.哈希函数  二、解决哈希冲突 1.闭散列  I,线性探测 II,二次探测 2.开散列 正文开始: 一、引言         哈希表是一种非常实用而且好用的关联式容器,如果你刷过不少题,

MyBatis学习——解决字段名与实体类属性名不相同的冲突

转载地址:http://www.cnblogs.com/xdp-gacl/p/4264425.html

HashMap中常用的函数

假设如下HashMap<String, Integer> map = new HashMap<>();获取value值1、返回key为a的valueget(a)2、返回key为a的value,若没有该key返回0getOrDefault(a,0)新增键值对1、新增键值对(a,1)put(a,1)2、如果key为a的键不存在,则存入键值对(a,1)putIfAbsent(a,1)3

hashmap的存值,各种遍历方法

package com.jefflee;import java.util.HashMap;import java.util.Iterator;import java.util.Map;public class HashmapTest {// 遍历Hashmap的四种方法public static void main(String[] args) {//hashmap可以存一个null,把

解决caffe 编译过程中protobuf版本冲突的问题

在编译caffe python3版本时一直会出现如下错误,(安装caffe python3具体方法可参考:https://blog.csdn.net/tingtie1438/article/details/82085199 ): 通过其错误信息可知是protobuf出了问题,现在网上教程一般都是默认安装的 libprotobuf-dev 和 protobuf-compiler,对于pytho

git 合并远程分支(带冲突)

应用场景 团队中两人同时fetch了一个分支。 第一个人修改后提交,第二个人提交就失败。失败信息如下: error: failed to push some refs to 'git@git.oschina.net:jacarrichan/jacarrichan.git'   hint: Updates were rejected because the remote contains

[VC] Visual Studio中读写权限冲突

前置场景: 编译没有报错,但是运行提示 内存异常: 情景1: 如下代码运行异常,提示引发了异常:写入权限冲突。*** 是 0xFFFFF..... char* str = (char*)malloc(10);str[0] = 0x30;  解决方案:要包含头文件<stdlib.h>  情景2: 在FileA文件调用FileB文件的函数,但是在FileA中却没有声明该B函数的原型

兔子--计算listview的高度,解决listview与scrollview控件冲突

/** * 计算ListView的高度 * * @param listView */ public void setListViewHeightBasedOnChildren(ListView listView) { // 获取ListView对应的Adapter OrderGoodsAdapter listAdapter = (OrderGoodsAdapter) listView.getAda

【Android】NestedScrollView的简单用法与滚动冲突、滑动冲突

一、NestedScrollView 1. 什么是 NestedScrollView NestedScrollView 是 Android 中一个用于处理垂直方向滚动的布局组件,它继承自 FrameLayout,同时支持嵌套滑动(Nested Scrolling)机制。相比于传统的 ScrollView,NestedScrollView 专为解决嵌套滚动冲突问题设计,能够与其他支持嵌套滑动的子