0%

javahashmap添加时遇到的问题

引入:为什么hashmap中添加时用了hashcode()还要用equals()去判断桶内部是否键对象相等,相等时替换旧值,而不相等时用头插法在链表头部插入新对象。

哈希码由于只有2 ^ 32个不同的整数,并且在任何VM实例中都可能有2 ^ 32个以上的活动对象,因此从技术上讲,不可能为每个对象保证唯一的哈希码。

即使默认哈希码可能基于对象的内部地址,也与内部地址不同。

如何保证哈希值的唯一性

1.哈希表(散列表)概述

hashCode()最终返回的是一个int 值,是有范围的,并且不是一个单映射结构。

哈希表结构:
对象数组+链表

存储原理:
哈希表在存储的时候会调用对象的hashCode值,拿到返回的int值,然后去跟默认数组的长度进行取余运算,得到一个具体的下标,这个下标用来决定这个对象究竟存到什么位置。

优点:
这样做的好处是根本不需要对数组进行遍历,通过哈希值取余运算就能很快找到数据存储的位置。

当取余得到的数据一样时,链表会依次往下存储,当哈希桶中的数据量大于8时,从链表转化为红黑二叉树,会更利于查找。当哈希桶中的数据减少到6时,又会从红黑树转化为链表。

补充:
问题:

已知我的链表哈希桶里现在有7个数据,它一定会从红黑树转为链表吗?

解答:

这个题的坑在于已知有7个数据,但是他并没有告诉你这时候是已经转成了红黑树,所以得一分为二的看待。

如果是一开始存数据,那么现在本身就还是链表,不存在再次转为链表的问题。

如果是已经转为了红黑树,现在数据减少到7,那么当数据减少到6的时候一定会转化为链表。

二、解决方法

1.必须重写hashCode()方法
解释:
每new一个对象就会有一个新的地址值,哈希码值几乎都不相等,那么在放进哈希桶中重复的几率就特别低。

比如:
new Person(“张三”,23); 0x123
new Person(“张三”,23); 0x124
虽然两个对象的值一样,但是,它们两个的地址不一样,所以哈希码值不一样,会放在不同的哈希桶中,不会去重。

作用:
1.只有重写了hashCode()方法才能调用equals()方法,因为子类不重写就继承父类,而父类(Object类)就是依据你的地址值进行计算的,但是每new一个对象就会有一个新的地址值,地址值不一样,得到的哈希码值一定不重复,所以一定不会调用equals()方法。因为只有出现同一个索引,才会使用equals()方法去对比。

2.尽可能少调用equals()方法 如果对象的属性值一样,那么尽可能让他们的哈希码值也一样,那么一定会调用equals()方法,这样就会去重了。
使用系统默认的hashCode()方法,减少equals()方法调用次数,提高效率。

2.必须重写equals()方法
解释:

equals()用来对比你的属性是否相同。当哈希值相同时调用equals()方法对比属性,如果属性相同,则认为是同一个对象,会去重。

注意:这两个方法只重写其中一个不去重,必须同时重写!

Java中的哈希表:

初始桶数量 : 16

散列因子 : 0.75 (哈希桶的数量是会变化的,当桶中已经有75%的地方存有数据了,那么对桶进行扩容,桶就会变得更多。扩容大小默认为原长度的两倍)

一、何为加载因子?

加载因子是表示Hsah表中元素的填满的程度.若:加载因子越大,填满的元素越多,好处是,空间利用率高了,但:冲突的机会加大了.反之,加载因子越小,填满的元素越少,好处是:冲突的机会减小了,但:空间浪费多了.

冲突的机会越大,则查找的成本越高.反之,查找的成本越小.因而,查找时间就越小.

因此,须在 “冲突的机会”与”空间利用率”之间寻找一种平衡与折衷. 这种平衡与折衷本质上是数据结构中有名的”时-空”矛盾的平衡与折衷.

二、HashMap中的加载因子

HashMap默认的加载因子是0.75,最大容量是16,因此可以得出HashMap的默认容量是:0.75*16=12。

用户可以自定义最大容量和加载因子。

HashMap 包含如下几个构造器:

  • HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。
  • HashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。
  • HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。