谈一谈HashMap类--key 为自定义对象,修改对象属性后
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()); } }
---
posted on 2019-03-09 17:48 wenbin_ouyang 阅读(932) 评论(0) 编辑 收藏 举报