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的更多相关文章

  1. 谈一谈HashMap类

    一.Java中的hashCode()和equals() 1. hashCode()的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode()是用来在散列存储结构中确定对 ...

  2. 谈一谈Java8的函数式编程(二) --Java8中的流

    流与集合    众所周知,日常开发与操作中涉及到集合的操作相当频繁,而java中对于集合的操作又是相当麻烦.这里你可能就有疑问了,我感觉平常开发的时候操作集合时不麻烦呀?那下面我们从一个例子说起. 计 ...

  3. 谈一谈泛型(Generic)

    谈一谈泛型 首先,泛型是C#2出现的.这也是C#2一个重要的新特性.泛型的好处之一就是在编译时执行更多的检查. 泛型类型和类型参数 ​ 泛型的两种形式:泛型类型( 包括类.接口.委托和结构 没有泛型枚 ...

  4. 谈一谈深度学习之semantic Segmentation

    上一次发博客已经是9月份的事了....这段时间公司的事实在是多,有写博客的时间都拿去看paper了..正好春节回来写点东西,也正好对这段时间做一个总结. 首先当然还是好好说点这段时间的主要工作:语义分 ...

  5. 谈一谈并查集QAQ(上)

    最近几日理了理学过的很多oi知识...发现不知不觉就有很多的知识忘记了... 在聊聊并查集的时候顺便当作巩固吧.... 什么是并查集呢? ( Union Find Set ) 是一种用于处理分离集合的 ...

  6. 谈一谈C#的事件

    谈一谈C#的事件 C#中事件基于委托,要理解事件要先理解委托,如果觉得自己关于委托不是很了解可以看看我前面写委托的文章 事件基于委托,是一种功能受限的委托,为委托提供了一种发布/订阅机制 使用委托时, ...

  7. Hashtable和HashMap类的区别

    Hashtable和HashMap类有三个重要的不同之处.第一个不同主要是历史原因.Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现. ...

  8. Java API —— HashMap类 & LinkedHashMap类

    1.HashMap类 1)HashMap类概述         键是哈希表结构,可以保证键的唯一性 2)HashMap案例         HashMap<String,String>   ...

  9. JAVA中的数据结构——集合类(线性表:Vector、Stack、LinkedList、set接口;键值对:Hashtable、Map接口<HashMap类、TreeMap类>)

    Java的集合可以分为两种,第一种是以数组为代表的线性表,基类是Collection:第二种是以Hashtable为代表的键值对. ... 线性表,基类是Collection: 数组类: person ...

随机推荐

  1. 【ContextLoaderListener】Web项目启动报错java.lang.ClassNotFoundException: ContextLoaderListener

    错误原因: 进入到tomcat的部署路径.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\下检查了一下,发现工程部署后在WE ...

  2. Directional RNA-seq data -which parameters to choose?

    Directional RNA-seq data -which parameters to choose? REF: https://chipster.csc.fi/manual/library-ty ...

  3. Vue.extend提供自定义组件的构造器

    Vue.extend 返回的是一个“扩展实例构造器”,也就是预设了部分选项的Vue实例构造器.经常服务于Vue.component用来生成组件,可以简单理解为当在模板中遇到该组件名称作为标签的自定义元 ...

  4. Jquery 点击事件重复获取叠加 (一)

    用jquery添加绑定事件 添加多少次 点击的时候就触发多少次 如果想解决这个问题 就在点击函数里先用 $(对象).off("click") 取消上一次的点击事件 上码: $(&q ...

  5. C#中引用第三方ocx控件引发的问题以及解决办法

    调用OCX控件的步骤:1.在系统中注册该ocx控件,命令:regsvr32.exe 控件位置(加 /u 参数是取消注册)2.在.net的工具箱中添加该控件,拖到form中去就可以了. 不用工具箱的话, ...

  6. 项目Alpha冲刺--4/10

    项目Alpha冲刺--4/10 1.团队信息 团队名称:基于云的胜利冲锋队 成员信息 队员学号 队员姓名 个人博客地址 备注 221500201 孙文慈 https://www.cnblogs.com ...

  7. unity shader base pass and additional pass

      [Unity Shaders]Shader中的光照,shadersshader 写在前面 自己写过Vertex & Fragment Shader的童鞋,大概都会对Unity的光照痛恨不已 ...

  8. C++图形开发相关

    1. WxWidgets 2. GTK+ 3. U++ Framework 4. QT

  9. 生成器的使用demo

    定义一个函数: def frange(start, stop, increment): x = start while x < stop: yield x x += increment 使用: ...

  10. html 进度条

    <html> <head> <title>进度条</title> <style type="text/css"> .co ...