谈一谈HashMap类2
1、由一个小案例引出本博文的讨论
public class Demo1 {
public static void main(String[] args) throws Exception {
Student s1 = new Student();
s1.setAge(10);
int capacity = 2; // HashMap集合的容量
HashMap<Student, String> stus = new HashMap<>(capacity, 1.0f);
stus.put(s1, "s1");
// 遍历HashMap
Set<Entry<Student, String>> entrySet = stus.entrySet();
for (Entry<Student, String> entry : entrySet) {
System.out.println(entry.getKey().getAge() + "-" + entry.getValue());
}
System.out.println("== 分割线 ==");
System.out.println("通过key获取value: " + stus.get(s1)); //s1
s1.setAge(20);
System.out.println("修改对象s1的age属性值后,通过key获取value: " + stus.get(s1)); // null
}
/**
* HashMap类(jdk1.7.0_60)中的方法:计算Object的hash值
*/
final static int hash(Object k) {
int h = 0;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
}
public class Student {
private String name;
private int age;
// getter和setter方法省略
@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;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) 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;
}
}
控制台打印结果:
10-s1
== 分割线 ==
通过key获取value: s1
修改对象s1的age属性值后,通过key获取value: null
分析:
1)自定义了一个Student类,重写写hashCode()和equals()方法;然后创建了一个HashMap集合,往集合中添加一个元素stus.put(s1, "s1"),其中key为Student类型的对象s1;
2)然后,查找集合中指定key的value值,即执行代码stus.get(s1),可以获取到value值;
3)此时,修改对象s1的age属性值,再次执行代码stus.get(s1),就不能获取到value值,这是为什么呢?
2、查看HashMap类(jdk1.7.0_60)源码
/**
* 根据key获取value值
*/
public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key); return null == entry ? null : entry.getValue();
}
/**
* 根据key获取Entry
*/
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
} int hash = (key == null) ? 0 : hash(key);
// indexFor(hash, table.length):根据key的hash值定位数组索引
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k; // 只有当e.hash == hash 并且 key相同时才能查找成功
// 那么e.hash == hash?
// 现在需要回答一个问题:修改Entry<key,value>的key的属性后,Entry的hash属性的值变吗?
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
3、修改Entry<key,value>的key的属性后,Entry的hash属性的值变吗?
public class Demo1 {
public static void main(String[] args) throws Exception {
Student s1 = new Student();
s1.setAge(10);
System.out.println("hash(s1) = " + hash(s1));
int capacity = 2; // HashMap集合的容量
HashMap<Student, String> stus = new HashMap<>(capacity, 1.0f);
stus.put(s1, "s1");
// 遍历HashMap
Set<Entry<Student, String>> entrySet = stus.entrySet();
for (Entry<Student, String> entry : entrySet) {
System.out
.println(entry.getKey().getAge() + "-" + entry.getValue() + " entry对象的hash属性的值:" + getHash(entry));
}
System.out.println("== 分割线 ==");
System.out.println("通过key获取value: " + stus.get(s1)); // s1
s1.setAge(20);
System.out.println("修改对象s1的age属性值后,hash(s1) = " + hash(s1));
System.out.println("修改对象s1的age属性值后,通过key获取value: " + stus.get(s1)); // null
for (Entry<Student, String> entry : entrySet) {
System.out
.println(entry.getKey().getAge() + "-" + entry.getValue() + " entry对象的hash属性的值:" + getHash(entry));
}
}
/**
* HashMap类(jdk1.7.0_60)中的方法:计算Object的hash值
*/
final static int hash(Object k) {
int h = 0;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**
* 自定义方法:通过反射获取Entry<?, ?>类型的对象的hash属性值
*/
public static int getHash(Entry<?, ?> entry) throws Exception {
Field field;
field = entry.getClass().getDeclaredField("hash");
field.setAccessible(true);
int hash = (int) field.get(entry);
return hash;
}
}
控制台打印结果:
hash(s1) = 1201
10-s1 entry对象的hash属性的值:1201
== 分割线 ==
通过key获取value: s1
修改对象s1的age属性值后,hash(s1) = 1603
修改对象s1的age属性值后,通过key获取value: null
20-s1 entry对象的hash属性的值:1201
分析:
根据控制台打印结果可知:修改Entry<key,value>中key的属性后,Entry的hash属性的值不变。
4、上面遇到的问题在put(key, value)也是一样的
public class Demo2 {
public static void main(String[] args) throws Exception {
Student s1 = new Student();
s1.setAge(10);
int capacity = 2; // HashMap集合的容量
HashMap<Student, String> stus = new HashMap<>(capacity, 1.0f);
stus.put(s1, "s1");
System.out.println("hash(s1)=" + hash(s1) + ", s1在哈希桶中存储的index:" + (hash(s1) % capacity));
Set<Entry<Student, String>> entrySet = stus.entrySet();
for (Entry<Student, String> entry : entrySet) {
System.out.println(entry.getKey().getAge() + "-" + entry.getValue() + " entry对象的hash属性的值:" + getHash(entry));
}
System.out.println("== 分割线 ==");
s1.setAge(20);
stus.put(s1, "s1");
System.out.println("hash(s1)=" + hash(s1) + ", 第二次添加s1在哈希桶中存储的index:" + (hash(s1) % capacity));
for (Entry<Student, String> entry : entrySet) {
System.out.println(entry.getKey().getAge() + "-" + entry.getValue() + " entry对象的hash属性的值:" + getHash(entry));
}
}
/**
* HashMap类中的方法:计算Object的hash值
*/
final static int hash(Object k) {
int h = 0;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**
* 自定义方法:通过反射获取Entry<?, ?>类型的对象的hash属性值
*/
public static int getHash(Entry<?, ?> entry) throws Exception {
Field field;
field = entry.getClass().getDeclaredField("hash");
field.setAccessible(true);
int hash = (int) field.get(entry);
return hash;
}
}
控制台打印结果:
hash(s1)=1201, s1在哈希桶中存储的index:1
10-s1 entry对象的hash属性的值:1201
== 分割线 ==
hash(s1)=1603, 第二次添加s1在哈希桶中存储的index:1
20-s1 entry对象的hash属性的值:1603
20-s1 entry对象的hash属性的值:1201
查看HashMap类(jdk1.7.0_60) put(key,value) 方法的源码
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
============================================================================
需要修改 s1 对象的属性,先从 HashMap 中删除,在重写放入
public static void main(String[] args) throws Exception {
Student s1 = new Student();
s1.setAge(10);
int capacity = 2; // HashMap集合的容量
HashMap<Student, String> stus = new HashMap<>(capacity, 1.0f);
stus.put(s1, "s1");
// 遍历HashMap
Set<Map.Entry<Student, String>> entrySet = stus.entrySet();
for (Map.Entry<Student, String> entry : entrySet) {
System.out.println(entry.getKey().getAge() + "-" + entry.getValue());
}
System.out.println("== 分割线 ==");
System.out.println("通过key获取value: " + stus.get(s1)); //s1
// 需要修改 s1 对象的属性,先从 HashMap 中删除,在重写放入
stus.remove(s1);
s1.setAge(20);
stus.put(s1, "s1");
System.out.println("修改对象s1的age属性值后,通过key获取value: " + stus.get(s1)); // s1
// 遍历HashMap
entrySet = stus.entrySet();
for (Map.Entry<Student, String> entry : entrySet) {
System.out.println(entry.getKey().getAge() + "-" + entry.getValue());
}
}
---
谈一谈HashMap类2的更多相关文章
- 谈一谈HashMap类
一.Java中的hashCode()和equals() 1. hashCode()的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode()是用来在散列存储结构中确定对 ...
- 谈一谈Java8的函数式编程(二) --Java8中的流
流与集合 众所周知,日常开发与操作中涉及到集合的操作相当频繁,而java中对于集合的操作又是相当麻烦.这里你可能就有疑问了,我感觉平常开发的时候操作集合时不麻烦呀?那下面我们从一个例子说起. 计 ...
- 谈一谈泛型(Generic)
谈一谈泛型 首先,泛型是C#2出现的.这也是C#2一个重要的新特性.泛型的好处之一就是在编译时执行更多的检查. 泛型类型和类型参数 泛型的两种形式:泛型类型( 包括类.接口.委托和结构 没有泛型枚 ...
- 谈一谈深度学习之semantic Segmentation
上一次发博客已经是9月份的事了....这段时间公司的事实在是多,有写博客的时间都拿去看paper了..正好春节回来写点东西,也正好对这段时间做一个总结. 首先当然还是好好说点这段时间的主要工作:语义分 ...
- 谈一谈并查集QAQ(上)
最近几日理了理学过的很多oi知识...发现不知不觉就有很多的知识忘记了... 在聊聊并查集的时候顺便当作巩固吧.... 什么是并查集呢? ( Union Find Set ) 是一种用于处理分离集合的 ...
- 谈一谈C#的事件
谈一谈C#的事件 C#中事件基于委托,要理解事件要先理解委托,如果觉得自己关于委托不是很了解可以看看我前面写委托的文章 事件基于委托,是一种功能受限的委托,为委托提供了一种发布/订阅机制 使用委托时, ...
- Hashtable和HashMap类的区别
Hashtable和HashMap类有三个重要的不同之处.第一个不同主要是历史原因.Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现. ...
- Java API —— HashMap类 & LinkedHashMap类
1.HashMap类 1)HashMap类概述 键是哈希表结构,可以保证键的唯一性 2)HashMap案例 HashMap<String,String> ...
- JAVA中的数据结构——集合类(线性表:Vector、Stack、LinkedList、set接口;键值对:Hashtable、Map接口<HashMap类、TreeMap类>)
Java的集合可以分为两种,第一种是以数组为代表的线性表,基类是Collection:第二种是以Hashtable为代表的键值对. ... 线性表,基类是Collection: 数组类: person ...
随机推荐
- 【字符串区别】SQLServer中char、varchar、nchar、nvarchar的区别:
一.定义 char: 固定长度,存储ANSI字符,不足的补英文半角空格. nchar: 固定长度,存储Unicode字符,不足的补英文半角空格 varchar: 可变长度,存储ANSI字符 ...
- js 二分搜索树删除子节点
删除的节点含有左子树或者右子树,用其子树来代替成为被删除节点的父节点的子树 删除左右都有孩子的节点,找到右边子树最小的节点作为父节点
- linux下gzip的压缩详解
Linux压缩保留源文件的方法: gzip -c filename > filename.gz Linux解压缩保留源文件的方法: gunzip -c filename.gz > file ...
- C# 各种控件实现可拖动和调整大小
http://www.360doc.com/content/18/0516/12/55659281_754382494.shtml using System; using System.Collect ...
- 记一次oracle创建一个新数据库,并导入正式环境数据库备份的dmp包过程
背景:正式环境oracle数据库定时用exp备份一个dmp包,现在打算在一台机器上创建一个新数据库,并导入这个dmp包. 1.创建数据库 开始 -> 所有程序 -> Oracle -> ...
- p3396 哈希冲突(暴力)
想了好久,没想到优秀的解法,结果是个暴力大吃一静.jpg 分类讨论,预处理\(p\le \sqrt{n}\) 的情况,其他直接暴力,复杂度\(O(n \sqrt{n} )\) #include < ...
- python2.7 qt4
https://jaist.dl.sourceforge.net/project/pyqt/PyQt4/PyQt-4.11.4/PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x64.e ...
- DIV+CSS+PS实现背景图的三层嵌套以及背景图的合并
传说中的“三层嵌套技术”. 一.背景图合并: div+css+ps合图相结合的技术:通过精确到1px的css设置,使用ps合成背景图片,特别是小图片合并,来完成页面效果. 首先讲讲三层 ...
- JqGrid 自定义子表格 及 自定义Json 格式数据不展示
项目第一次使用JqGrid ,发现功能强大,但由于对他不熟悉,也没有少走弯路,记录一下. 1.引用 <link href="~/Scripts/JqGrid/jqgrid/css/ui ...
- 利用angularjs完成注册表单
ng-init="username = 'first'"设置初始显示first字段 ng-class="{'error':signUpForm.username.$inv ...