
Java 手撕HashMap
发布日期:2021-05-07 03:01:00
浏览次数:24
分类:精选文章
本文共 5680 字,大约阅读时间需要 18 分钟。
Map的相关学习
HashMap
Interface Map<K,V> 部分实现类:
- LinkHashMap不允许重复,记录顺序
- Hashtable底层是一个哈希表,是一个线程的安全集合,速度慢(不可以存 Null)
- HashMap底层哈希表,多线程不安全的,速度快。可以存取 Null
类似还有 TreeMap:默认按照 key递增排序 entry实体。当然也可以重写比较器,可以参考前面文章。
HashCode与Equals关系
关于HashCode的官方文档定义:
hashcode方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable 提供的哈希表。 hashCode 的常规协定是: 在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。 如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。 以下情况不是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。 实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。) 当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
以上这段官方文档的定义,我们可以抽出成以下几个关键点:
1、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;
2、如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;
3、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;
4、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。
再归纳一下就是hashCode是用于查找使用的,而equals是用于比较两个对象的是否相等的。
1.hashcode是用来查找的,如果你学过数据结构就应该知道,在查找和排序这一章有例如内存中有这样的位置0 1 2 3 4 5 6 7 而我有个类,这个类有个字段叫ID,我要把这个类存放在以上8个位置之一,如果不用hashcode而任意存放,那么当查找时就需要到这八个位置里挨个去找,或者用二分法一类的算法。但如果用hashcode那就会使效率提高很多。我们这个类中有个字段叫ID,那么我们就定义我们的hashcode为ID%8,然后把我们的类存放在取得得余数那个位置。比如我们的ID为9,9除8的余数为1,那么我们就把该类存在1这个位置,如果ID是13,求得的余数是5,那么我们就把该类放在5这个位置。这样,以后在查找该类时就可以通过ID除 8求余数直接找到存放的位置了。2.但是如果两个类有相同的hashcode怎么办那(我们假设上面的类的ID不是唯一的),例如9除以8和17除以8的余数都是1,那么这是不是合法的,回答是:可以这样。那么如何判断呢?在这个时候就需要定义 equals了。也就是说,我们先通过 hashcode来判断两个类是否存放某个桶里,但这个桶里可能有很多类,那么我们就需要再通过 equals 来在这个桶里找到我们要的类。那么。重写了equals(),为什么还要重写hashCode()呢?想想,你要在一个桶里找东西,你必须先要找到这个桶啊,你不通过重写hashcode()来找到桶,光重写equals()有什么用啊
注意:
- equals方法:默认调用Object类的equals方法,即比较两个对象的引用地址是否相同
- 重写了hashcode方法后,也需要重写equals方法
比如:
创建两个Person类对象,判断二者是否为同一对象,或者是否重复(排除重名)public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } // 重写hashcode方法 public int hashCode() { return age % 10; } public final static void main(String[] args) { Person p1 = new Person("猪猪侠", 18); Person p2 = new Person("猪猪侠", 18); Setset = new HashSet (); set.add(p1); set.add(p2); System.out.println(p1.hashCode() == p2.hashCode()); System.out.println(p1.equals(p2)); System.out.println(set); }}
结果是:
true
false set 包含两个人
然而我们认为p1 p2是重复的,因此 需要重写equals方法。
摘自博客
感谢!重写HashCode与Equals方法
- HashMap存储自定义的键Value:
- String类重写了HashCode和Equals方法,可以保证Key唯一
示例:
public class Person{ private String name; private int age; /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Person[name=" + name + ", age=" + age + "]"; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; // 处理冲突,数据结构中有介绍 result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } /** * */ public Person() { super(); } /** * @param name * @param age */ public Person(String name, int age) { super(); this.name = name; this.age = age; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } /** * @return the age */ public int getAge() { return age; } /** * @param age the age to set */ public void setAge(int age) { this.age = age; }}
现在两个对象就完全相等了,上面的HashSet中也只存放了一个对象。
Map的迭代器
Map集合没有实现Iterable接口,所以map集合不能直接使用增强for循环,如果需要使用增强for循环需要借助于Collection的集合。
Map.Entry< K,V >
- 地图条目(键值对)/(实体集)。Map.entrySet()方法返回地图的集合视图,其元素属于此类。 获取对映射条目的引用的唯一方法是从该集合视图的迭代器。
- 这些Map.Entry对象仅在迭代期间有效; 更正式地,如果在迭代器返回条目之后修改了后备映射,则映射条目的行为是未定义的,除了通过映射条目上的setValue操作。
- Map.entrySet() 方法 返回的是:Set<Map.Entry<K,V>> 对象。将map中的每个entry对象(键值对)取出来,放进set集合中
// 创建HashMap集合对象,使用多态Mapmap=new HashMap ();// 使用put方法进行添加元素map.put("帅",521);map.put("柳",520);// 使用Map集合中的entrySet方法,将map中的每个entry对象取出来,放进set集合中Set > set=map.entrySet();
遍历方法:
- 使用迭代器遍历集合
// 遍历set集合获取每一个Entry对象,使用迭代器遍历集合Iterator> it = set.iterator();while(it.hasNext()){ // Map.Entry 获取实体对象 Map.Entry entry=it.next(); // 使用entry方法getKey和getValue获取键和值 String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key+value);}
- 使用增强for遍历
//使用增强for,使用entry方法getKey和getValue获取键和值for(Map.Entryentry: set){ String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key+value);}// 或者使用 Entry Set > sets = maps.entrySet();for(Entry key: sets){ String value = maps.get(key); // 获取Value值 System.out.println(key+value);}
- keySet() + for循环遍历
//使用KeySet和for遍历Map集合 .使用Map集合中的keySet方法将map中的key放入Set集合中,Setset = map.keySet();for(String key: set){ int value=map.get(key);//获取Value值 System.out.println(key+value);}
借鉴学习大神们的博客
感谢!参考大佬文章,及官方API,向优秀的人致敬!
加油!!!发表评论
最新留言
哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年03月26日 19时38分42秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Requests实践详解
2019-03-06
接口测试简介
2019-03-06
Golang Web入门(4):如何设计API
2019-03-06
让sublime实现js控制台(前提是安装了nodejs)
2019-03-06
树莓派连接二手液晶屏小记
2019-03-06
error: 'LOG_TAG' macro redefined
2019-03-06
android10Binder(一)servicemanager启动流程
2019-03-06
ES6基础之——new Set
2019-03-06
nodeJS实现识别验证码(tesseract-ocr+GraphicsMagick)
2019-03-06
玩玩小爬虫——试搭小架构
2019-03-06
AS与.net的交互——加载web上的xml
2019-03-06
Javascript之旅——第八站:说说instanceof踩了一个坑
2019-03-06
Javascript之旅——第九站:吐槽function
2019-03-06
Javascript之旅——第十一站:原型也不好理解?
2019-03-06
Sql Server之旅——第十站 看看DML操作对索引的影响
2019-03-06
十五天精通WCF——第二天 告别烦恼的config配置
2019-03-06
双十一来了,别让你的mongodb宕机了
2019-03-06
asp.net mvc 之旅 —— 第六站 ActionFilter的应用及源码分析
2019-03-06
Tomcat 热部署
2019-03-06
深入解析 HTTP 缓存控制
2019-03-06