1、由一个小案例引出本博文的讨论

  1. public class Demo1 {
  2. public static void main(String[] args) throws Exception {
  3. Student s1 = new Student();
  4. s1.setAge(10);
  5.  
  6. int capacity = 2; // HashMap集合的容量
  7. HashMap<Student, String> stus = new HashMap<>(capacity, 1.0f);
  8. stus.put(s1, "s1");
  9.  
  10. // 遍历HashMap
  11. Set<Entry<Student, String>> entrySet = stus.entrySet();
  12. for (Entry<Student, String> entry : entrySet) {
  13. System.out.println(entry.getKey().getAge() + "-" + entry.getValue());
  14. }
  15. System.out.println("== 分割线 ==");
  16.  
  17. System.out.println("通过key获取value: " + stus.get(s1)); //s1
  18. s1.setAge(20);
  19. System.out.println("修改对象s1的age属性值后,通过key获取value: " + stus.get(s1)); // null
  20. }
  21.  
  22. /**
  23. * HashMap类(jdk1.7.0_60)中的方法:计算Object的hash值
  24. */
  25. final static int hash(Object k) {
  26. int h = 0;
  27. if (0 != h && k instanceof String) {
  28. return sun.misc.Hashing.stringHash32((String) k);
  29. }
  30. h ^= k.hashCode();
  31. h ^= (h >>> 20) ^ (h >>> 12);
  32. return h ^ (h >>> 7) ^ (h >>> 4);
  33. }
  34. }
  1. public class Student {
  2. private String name;
  3. private int age;
  4. // getter和setter方法省略
  5.  
  6. @Override
  7. public int hashCode() {
  8. final int prime = 31;
  9. int result = 1;
  10. result = prime * result + age;
  11. result = prime * result + ((name == null) ? 0 : name.hashCode());
  12. return result;
  13. }
  14.  
  15. @Override
  16. public boolean equals(Object obj) {
  17. if (this == obj)
  18. return true;
  19. if (obj == null)
  20. return false;
  21. if (getClass() != obj.getClass())
  22. return false;
  23. Student other = (Student) obj;
  24. if (age != other.age)
  25. return false;
  26. if (name == null) {
  27. if (other.name != null)
  28. return false;
  29. } else if (!name.equals(other.name))
  30. return false;
  31. return true;
  32. }
  33. }

控制台打印结果:

  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)源码

  1. /**
  2. * 根据key获取value值
  3. */
  4. public V get(Object key) {
  5. if (key == null)
  6. return getForNullKey();
  7. Entry<K,V> entry = getEntry(key);
  8.  
  9. return null == entry ? null : entry.getValue();
  10. }
  11. /**
  12. * 根据key获取Entry
  13. */
  14. final Entry<K,V> getEntry(Object key) {
  15. if (size == 0) {
  16. return null;
  17. }
  18.  
  19. int hash = (key == null) ? 0 : hash(key);
  20. // indexFor(hash, table.length):根据key的hash值定位数组索引
  21. for (Entry<K,V> e = table[indexFor(hash, table.length)];
  22. e != null;
  23. e = e.next) {
  24. Object k;
  25.  
  26. // 只有当e.hash == hash 并且 key相同时才能查找成功
  27. // 那么e.hash == hash?
  28. // 现在需要回答一个问题:修改Entry<key,value>的key的属性后,Entry的hash属性的值变吗?
  29. if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
  30. return e;
  31. }
  32. return null;
  33. }

3、修改Entry<key,value>的key的属性后,Entry的hash属性的值变吗?

  1. public class Demo1 {
  2. public static void main(String[] args) throws Exception {
  3. Student s1 = new Student();
  4. s1.setAge(10);
  5. System.out.println("hash(s1) = " + hash(s1));
  6.  
  7. int capacity = 2; // HashMap集合的容量
  8. HashMap<Student, String> stus = new HashMap<>(capacity, 1.0f);
  9. stus.put(s1, "s1");
  10.  
  11. // 遍历HashMap
  12. Set<Entry<Student, String>> entrySet = stus.entrySet();
  13. for (Entry<Student, String> entry : entrySet) {
  14. System.out
  15. .println(entry.getKey().getAge() + "-" + entry.getValue() + " entry对象的hash属性的值:" + getHash(entry));
  16. }
  17. System.out.println("== 分割线 ==");
  18.  
  19. System.out.println("通过key获取value: " + stus.get(s1)); // s1
  20. s1.setAge(20);
  21. System.out.println("修改对象s1的age属性值后,hash(s1) = " + hash(s1));
  22. System.out.println("修改对象s1的age属性值后,通过key获取value: " + stus.get(s1)); // null
  23. for (Entry<Student, String> entry : entrySet) {
  24. System.out
  25. .println(entry.getKey().getAge() + "-" + entry.getValue() + " entry对象的hash属性的值:" + getHash(entry));
  26. }
  27. }
  28.  
  29. /**
  30. * HashMap类(jdk1.7.0_60)中的方法:计算Object的hash值
  31. */
  32. final static int hash(Object k) {
  33. int h = 0;
  34. if (0 != h && k instanceof String) {
  35. return sun.misc.Hashing.stringHash32((String) k);
  36. }
  37. h ^= k.hashCode();
  38. h ^= (h >>> 20) ^ (h >>> 12);
  39. return h ^ (h >>> 7) ^ (h >>> 4);
  40. }
  41.  
  42. /**
  43. * 自定义方法:通过反射获取Entry<?, ?>类型的对象的hash属性值
  44. */
  45. public static int getHash(Entry<?, ?> entry) throws Exception {
  46. Field field;
  47. field = entry.getClass().getDeclaredField("hash");
  48. field.setAccessible(true);
  49. int hash = (int) field.get(entry);
  50. return hash;
  51. }
  52. }

控制台打印结果:

  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)也是一样的

  1. public class Demo2 {
  2. public static void main(String[] args) throws Exception {
  3. Student s1 = new Student();
  4. s1.setAge(10);
  5.  
  6. int capacity = 2; // HashMap集合的容量
  7. HashMap<Student, String> stus = new HashMap<>(capacity, 1.0f);
  8. stus.put(s1, "s1");
  9. System.out.println("hash(s1)=" + hash(s1) + ", s1在哈希桶中存储的index:" + (hash(s1) % capacity));
  10.  
  11. Set<Entry<Student, String>> entrySet = stus.entrySet();
  12. for (Entry<Student, String> entry : entrySet) {
  13. System.out.println(entry.getKey().getAge() + "-" + entry.getValue() + " entry对象的hash属性的值:" + getHash(entry));
  14. }
  15. System.out.println("== 分割线 ==");
  16.  
  17. s1.setAge(20);
  18. stus.put(s1, "s1");
  19. System.out.println("hash(s1)=" + hash(s1) + ", 第二次添加s1在哈希桶中存储的index:" + (hash(s1) % capacity));
  20. for (Entry<Student, String> entry : entrySet) {
  21. System.out.println(entry.getKey().getAge() + "-" + entry.getValue() + " entry对象的hash属性的值:" + getHash(entry));
  22. }
  23. }
  24.  
  25. /**
  26. * HashMap类中的方法:计算Object的hash值
  27. */
  28. final static int hash(Object k) {
  29. int h = 0;
  30. if (0 != h && k instanceof String) {
  31. return sun.misc.Hashing.stringHash32((String) k);
  32. }
  33. h ^= k.hashCode();
  34. h ^= (h >>> 20) ^ (h >>> 12);
  35. return h ^ (h >>> 7) ^ (h >>> 4);
  36. }
  37.  
  38. /**
  39. * 自定义方法:通过反射获取Entry<?, ?>类型的对象的hash属性值
  40. */
  41. public static int getHash(Entry<?, ?> entry) throws Exception {
  42. Field field;
  43. field = entry.getClass().getDeclaredField("hash");
  44. field.setAccessible(true);
  45. int hash = (int) field.get(entry);
  46. return hash;
  47. }
  48. }

控制台打印结果:

  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) 方法的源码

  1. public V put(K key, V value) {
  2. if (table == EMPTY_TABLE) {
  3. inflateTable(threshold);
  4. }
  5. if (key == null)
  6. return putForNullKey(value);
  7. int hash = hash(key);
  8. int i = indexFor(hash, table.length);
  9. for (Entry<K,V> e = table[i]; e != null; e = e.next) {
  10. Object k;
  11. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
  12. V oldValue = e.value;
  13. e.value = value;
  14. e.recordAccess(this);
  15. return oldValue;
  16. }
  17. }
  18.  
  19. modCount++;
  20. addEntry(hash, key, value, i);
  21. return null;
  22. }

============================================================================

  需要修改 s1 对象的属性,先从 HashMap 中删除,在重写放入

  1. public static void main(String[] args) throws Exception {
  2. Student s1 = new Student();
  3. s1.setAge(10);
  4.  
  5. int capacity = 2; // HashMap集合的容量
  6. HashMap<Student, String> stus = new HashMap<>(capacity, 1.0f);
  7. stus.put(s1, "s1");
  8.  
  9. // 遍历HashMap
  10. Set<Map.Entry<Student, String>> entrySet = stus.entrySet();
  11. for (Map.Entry<Student, String> entry : entrySet) {
  12. System.out.println(entry.getKey().getAge() + "-" + entry.getValue());
  13. }
  14. System.out.println("== 分割线 ==");
  15.  
  16. System.out.println("通过key获取value: " + stus.get(s1)); //s1
  17.  
  18. // 需要修改 s1 对象的属性,先从 HashMap 中删除,在重写放入
  19. stus.remove(s1);
  20. s1.setAge(20);
  21. stus.put(s1, "s1");
  22.  
  23. System.out.println("修改对象s1的age属性值后,通过key获取value: " + stus.get(s1)); // s1
  24.  
  25. // 遍历HashMap
  26. entrySet = stus.entrySet();
  27. for (Map.Entry<Student, String> entry : entrySet) {
  28. System.out.println(entry.getKey().getAge() + "-" + entry.getValue());
  29. }
  30.  
  31. }

---

谈一谈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. 【重新分配分片】Elasticsearch通过reroute api重新分配分片

    elasticsearch可以通过reroute api来手动进行索引分片的分配. 不过要想完全手动,必须先把cluster.routing.allocation.disable_allocation ...

  2. ssm项目中遇到微信用户名称带有表情,插入失败问题

    ssm项目中遇到微信用户名称带有表情,插入失败问题 问题 Mysql的utf8编码最多3个字节,而Emoji表情或者某些特殊字符是4个字节. 因此会导致带有表情的昵称插入数据库时出错. 解决方法 一. ...

  3. 关不掉.vbs

    创建: 1.在桌面新建一个 关不掉.txt 文本文档 2.打开输入一下内容 do msgbox"信不信你关不掉我" msgbox"哈哈,你相信了吧" msgbo ...

  4. k8s2

    1.主节点与子节点如何沟通,交互 apiServer <==> kublet 2. pod之间如何共享, 使用volumn(数据卷 ) kube-proxy 和 service 配置好网络 ...

  5. kubernetes 实战4_命令_Configure Pods and Containers

    Configure Service Accounts for Pods A service account provides an identity for processes that run in ...

  6. Lintcode415-Valid Palindrome-Medium

    Given a string, determine if it is a palindrome, considering only alphanumeric(字母和数字) characters and ...

  7. 【Selenium2】【selenium之 定位以及切换frame(iframe)】

    参考:http://blog.csdn.net/huilan_same/article/details/52200586 总有人看不明白,以防万一,先在开头大写加粗说明一下: frameset不用切, ...

  8. P1182 数列分段`Section II`

    传送门 思路: 求数列每段和的最大值的最小值,很明显是用二分法求解,加贪心检验.本题关键是要怎么去高效的check,可以考虑一个贪心的思路,能加的就加上,不能则新开一段,so对于二分的值 u ,我们从 ...

  9. zipkin启动报错(Caused by: java.lang.ClassNotFoundException: zipkin.Component)的解决方法

    使用ziplin依赖: <dependency> <groupId>org.springframework.cloud</groupId> <artifact ...

  10. 在未排序的数组中找到第 k 个最大的元素

    在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6,4] 和 k = 2 输出: 5 ...