摘要:放入第二个元素,再计算哈希值,发现与第一个节点的哈希值相同。覆盖又来一个,哈希值相等,长度为,与是相等的,直接将原节点的值覆盖。第五个,哈希值相等,长度为,与相等,覆盖,由修改为。
问题描述
一道来自Java官方twitter的问题。风格很像目前国内各大互联网公司的笔试题。
public class MapEqualsChallenge { public static void main(String[] args) { Mapmap = new LinkedHashMap<>(); map.put(new Stark("Arya"), "1"); map.put(new Stark("Ned"), "2"); map.put(new Stark("Sansa"), "3"); map.put(new Stark("Bran"), "4"); map.put(new Stark("Jaime"), "5"); map.forEach((key, value) -> System.out.println(value)); } static class Stark { String name; public Stark(String name) { this.name = name; } @Override public boolean equals(Object obj) { return ((Stark) obj).name.length() == this.name.length(); } @Override public int hashCode() { return 4000 << 2 * 2000 / 10000; } } }
挺有意义的一道题,绝对是学习equals和hashcode的经典案例,为官方社区点个赞。
分析 Java 集合这是一个有关Java集合的关系图,最初觉得这个好复杂,但是现在再去看看,其实也很简单。
Collection和Map接口都是我们常用的,这个图有一点问题,在JDK的源码中,Map和Collection毫无关系。
List:强调数据在集合中的位置。
Set:强调集合中元素不允许重复。
Map:强调集合中的元素根据key来查询。
如果你不明白三者的区别与联系,请看《Head First Java 第二版》557页,书中用图示对三者进行了详细的阐述。
LinkedHashMap之前研究过HashMap和ConcurrentHashMap(线程安全的HashMap),这个LinkedHashMap倒是第一次见。
LinkedHashMap继承自HashMap,二者区别不大。
存放数据,根据key计算哈希值,然后根据哈希值计算该元素应当存储在数组中的位置,如果hash冲突,并且不是一个对象,则用链表处理。
HashMap实现的是单向链表,LinkedHashMap实现的是双向链表。
hashCode这是源码中计算哈希值的方法,先取对象的哈希值,然后再进行相应的处理。
所以再去看put过程:key是一个对象,然后LinkedHashMap会调用key的hashCode方法。
map.put(new Stark("Arya"), "1");
我们可能一看这个hashCode方法就吓蒙了,这怎么算啊?其实我们完全没必要去计算这个表达式,这是个常量表达式,所以不管new出来多少个对象,哈希值都是一样的。再去调用LinkedHashMap的hash方法,返回值也是一样。
@Override public int hashCode() { return 4000 << 2 * 2000 / 10000; }
先不去管到底是双向链表还是单向链表,反正哈希值相同,这些个键值对肯定在一个链表上。
map.put(new Stark("Arya"), "1");
执行第一行代码,插入key为Arya对象,value为1的节点对象。
equalsmap.put(new Stark("Ned"), "2");
放入第二个元素,再计算哈希值,发现与第一个节点key的哈希值相同。
如果两个对象的哈希值相等,则这两个对象有可能相等;如果两个对象的哈希值不相等,那这两个对象肯定不相等。
哈希值相等,然后用equals方法判断两个对象是否相等。
@Override public boolean equals(Object obj) { return ((Stark) obj).name.length() == this.name.length(); }
重写过的equals是根据对象名字的长度来判等的,如果两个对象名字长度相等,那就认为是同一对象。
Arya与Ned长度不相等,所以LinkedHashMap认为这是两个对象,再去新建一个Node,指过去。
注意:这里有一个插入的位置问题,到底是在链表头部插新节点还是在尾部插新节点?据说面试官还考过?
答案是:在Java8之前,是在头部插入节点;在Java8中,是在尾部插入节点。
文章链接:HashMap到底是插入链表头部还是尾部
map.put(new Stark("Sansa"), "3");
又来一个,哈希值相等,名字长度为5,没有一样的key,再插一个新节点。
覆盖map.put(new Stark("Bran"), "4");
又来一个,哈希值相等,长度为4,与Arya 1是相等的,直接将原节点的值覆盖。由1修改为4。
map.put(new Stark("Jaime"), "5");
第五个,哈希值相等,长度为5,与Sansa 3相等,覆盖,由3修改为5。
结果分析了一大堆,切换到IDE中运行,与预期结果一致。
总结学然后知不足,教然后知困!
之前觉得自己对HashMap还是听明白的,但当自己去画各种示意图时,发现还有很多细节去需要学习。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/72061.html
摘要:类的源码对象的方法其中会消耗大量时间。所以,如果在基于哈希表的容器中存储对象,简直就是灾难。下面这段代码,对比了和在存储次时的表现输出为所以,基于哈希表实现的容器最好不要用。这也给我们启发结尾的最好还是加上以上,本周末发现的一些坑。 背景介绍 最近再做一个RSS阅读工具给自己用,其中一个环节是从服务器端获取一个包含了RSS源列表的json文件,再根据这个json文件下载、解析RSS内容...
摘要:我们使用调试器却发现在中已经存储了这个对象。中和有一个契约如果两个对象相等的话,它们的必须相等但如果两个对象的相等的话,这两个对象不一定相等。的结构能够快速找到一个对象,而不是进行较慢的线性查找。可以看作是数组的数组。 java.lang.Object类中有两个非常重要的方法: public boolean equals(Object obj) public int hashCode...
摘要:所以利用哈希表这种数据结构实现具体类时,需要设计个好的函数,使冲突尽可能的减少其次是需要解决发生冲突后如何处理。源码剖析首先从构造函数开始讲,遵循集合框架的约束,提供了一个参数为空的构造函数与有一个参数且参数类型为的构造函数。 本文章首发于个人博客,鉴于sf博客样式具有赏心悦目的美感,遂发表于此,供大家学习、批评。本文还在不断更新中,最新版可移至个人博客。? 继上一篇文章Java集合...
摘要:在中对象是一切对象都会自动继承的一个类,在这个类中定义的属性和方法可以说是每个类都必须的。这里有必要说说这里对象里面的几个方法返回该对象的哈希码值。这些基于表的集合,只能要求被存放的对象实现自己的方法,保证的均匀性。 Object 在Java中Object对象是一切对象都会自动继承的一个类,在这个类中定义的属性和方法可以说是每个类都必须的。 这里有必要说说这里对象里面的几个方法 has...
摘要:继承的类,泛型为时,注意和其他的类型不同。因为是线程安全简单来说,是个一维数组。同样,指定和,如果中间发生变化则会抛出异常。最后,可以,然后,使用基类可以实现和的快速赋值。线程安全也是线程安全的,和一样,连函数都丧心病狂地同步了。 这么几个比较常用的但是比较容易混淆的概念同出于 java.util 包。本文仅作几个类的浅度解析。 (本文基于JDK1.7,源码来自openjdk1.7。)...
阅读 3337·2022-01-04 14:20
阅读 3106·2021-09-22 15:08
阅读 2187·2021-09-03 10:44
阅读 2314·2019-08-30 15:44
阅读 1490·2019-08-29 18:40
阅读 2654·2019-08-29 17:09
阅读 2988·2019-08-26 13:53
阅读 3220·2019-08-26 13:37