首页 > 编程知识 正文

java字符串hash值相同,java hashcode

时间:2023-05-03 11:15:19 阅读:138246 作者:2728

扩展:创建两倍于原始长度的新Entry空数组。 遍历原始Entry数组,并将所有Entry重新散列到新数组。 随着长度的增加,Hash的规则会发生变动,所以需要重新进行Hash。

让我们回顾一下Hash公式。 index=hashcode(key ) (Length - 1 ) ) ) ) ) ) ) index=hashcode(key ) ) ) ) ) ) ) ) ) index=hashcode(key ) ) )

在原数组长度为8的情况下,Hash运算是和111B的乘积运算; 新数组的长度为16,Hash运算是与1111B的乘积运算。 Hash的结果明显不同。

Resize之前的HashMap :

Resize后的HashMap:

ReHash的Java代码如下:

/** * Transfers all entries from current table to newTable. */void transfer(Entry[] newTable, boolean rehash) { int newCapacity = newTable.length; for (Entry<K,V> e : table) { while(null != e) { Entry<K,V> next = e.next; if (rehash) { e.hash = null == e.key ? 0 : hash(e.key); } int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } }} HashMap线程问题(参考JDK1.7)

上面的情况使用在单线程下是没有问题的,但是一旦使用在多线程中,那么就会出现破坏内部数据结构的链表数组。其中一些链接可能会丢失,或者形成了回路,从而导致数据结构不可用。在ConcurrentHashMap中是不会发生的,高并发的情况下使用这个集合类兼顾了线程安全和性能。为保证线程安全可以使用HashTable、collections、synchronizedMap。

内容包括:

Hashmap在插入元素过多的时候需要进行Resize,Resize的条件是
HashMap.Size >= Capacity * LoadFactor。

Hashmap的Resize包含扩容和ReHash两个步骤,ReHash在并发的情况下可能会形成链表环。

原理:

假设一个HashMap已经到了Resize的临界点。此时有两个线程A和B,在同一时刻对HashMap进行Put操作:


此时达到Resize条件,两个线程各自进行Rezie的第一步,也就是扩容:

这时候,两个线程都走到了ReHash的步骤。让我们回顾一下ReHash的代码:

假如此时线程B遍历到Entry3对象,刚执行完红框里的这行代码,线程就被挂起。对于线程B来说:

e = Entry3
next = Entry2

这时候线程A畅通无阻地进行着Rehash,当ReHash完成后,结果如下(图中的e和next,代表线程B的两个引用):

直到这一步,看起来没什么毛病。接下来线程B恢复,继续执行属于它自己的ReHash。线程B刚才的状态是:

e = Entry3
next = Entry2

当执行到上面这一行时,显然 i = 3,因为刚才线程A对于Entry3的hash结果也是3。

我们继续执行到这两行,Entry3放入了线程B的数组下标为3的位置,并且e指向了Entry2。此时e和next的指向如下:

e = Entry2
next = Entry2

整体情况如图所示:

接着是新一轮循环,又执行到红框内的代码行:

e = Entry2
next = Entry3

整体情况如图所示:

接下来执行下面的三行,用头插法把Entry2插入到了线程B的数组的头结点:

整体情况如图所示:

第三次循环开始,又执行到红框的代码:

e = Entry3
next = Entry3.next = null

最后一步,当我们执行下面这一行的时候,见证奇迹的时刻来临了:

newTable[i] = Entry2
e = Entry3
Entry2.next = Entry3
Entry3.next = Entry2

链表出现了环形!

整体情况如图所示:

此时,问题还没有直接产生。当调用Get查找一个不存在的Key,而这个Key的Hash结果恰好等于3的时候,由于位置3带有环形链表,所以程序将会进入死循环!

ConcurrentHashMap的出现确保了线程的安全且高效率 这里介绍的ConcurrentHashMap原理和代码,都是基于Java1.7的。在Java8中会有些许差别。ConcurrentHashMap在对Key求Hash值的时候,为了实现Segment均匀分布,进行了两次Hash。有兴趣的朋友可以研究一下源代码。 String中的HashCode()

String类有个私有的实例字段hash表示这串受伤的镜子值,第一次调用的时候,字符串的受伤的镜子值会被计算并且赋值给Hash字段,之后再调用HashCode的方法直接取hash字段返回。算法中的方式是,以31为乘法算式中的因数,再和每个字符进行ASCII码对应值作运算。

public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h;}

字符串受伤的镜子可以做很多事情,通常是类似于字符串判等,判回文之类的。

但是仅仅依赖于受伤的镜子值来判断其实是不严谨的,除非能够保证不会有受伤的镜子冲突,通常这一点很难做到。

就拿jdk中String类的受伤的镜子方法来举例,字符串”gdejicbegh”与字符串”hgebcijedg”具有相同的hashCode()返回值-801038016,并且它们具有reverse的关系。这个例子说明了用jdk中默认的hashCode方法判断字符串相等或者字符串回文,都存在反例。

部分的原理来自数据算法与数据结构和程序员rzdjy的文章观点,针对某些原理本人进行一些个人的理解,大部分自己进行了深入的理解和探索。对某些问题进行了许多补充。学习是我们开始研究并且进行创造的基本需要,我们引用学习并且使用这些基础进行延伸的创造和提供价值,这是我们人类历史上进化的必需过程。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。