谈一谈HashMap类2

时间:2022-09-15 09:26:53

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&num;的事件

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

  7. Hashtable和HashMap类的区别

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

  8. Java API —— HashMap类 &amp&semi; LinkedHashMap类

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

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

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

随机推荐

  1. 总结javascript中的全局变量和局部变量的区别以及声明函数和调用函数的区别

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="utf-8&quo ...

  2. java连接mysql数据库详细步骤解析

    java连接mysql数据库详细步骤解析      第一步:下载一个JDBC驱动包,例如我用的是:mysql-connector-java-5.1.17-bin.jar      第二步:导入下载的J ...

  3. 循序渐进Python3(四) -- 装饰器、迭代器和生成器

    初识装饰器(decorator ) Python的 decorator 本质上就是一个高阶函数,它接收一个函数作为参数,然后,返回一个新函数. 使用 decorator 用Python提供的 @ 语法 ...

  4. Hadoop第8周练习—Pig部署及统计访问日志例子

    :搭建Pig环境 :计算每个IP点击次数 内容 运行环境说明 1.1     硬软件环境 线程,主频2.2G,6G内存 l  虚拟软件:VMware® Workstation 9.0.0 build- ...

  5. poj 1664

    http://poj.org/problem?id=1664 题目是中文的,一个递归的题目 把每一次的苹果分为两类 Ⅰ:所以盘子都放一个,然后其他的在随便放: Ⅱ:有一个盘子没有放苹果: 这样下去的话 ...

  6. oracle报表开发方案

    PL/SQL也是一门语言,后台开发经常会用到. 目前做要到一个功能,关于"报表任务调度",说明白了就是做几张报表,每天统计一次新数据,用于在PC页面上显示,我苦思冥想了几天,总结出 ...

  7. 第一章开发简单的Java应用程序

    1.什么是程序? 程序一词来源于生活,通俗点讲就是把生活的的事用程序编写出来 并执行. 2.为什么要学习Java呢? Java是Sun Microsystems于1995年推出的高级编程语言 Java ...

  8. Java8过滤器&lpar;Filter&rpar;

    1.在Java之前对List进行过滤的方式 public class BeforeJava8 { public static void main(String[] args) { List<Pe ...

  9. day4——无重复字符的最长子串

    // 小白一名,0算法基础,艰难尝试算法题中,若您发现本文中错误, 或有其他见解,往不吝赐教,感激不尽,拜谢. 领扣 第2题 今日算法 题干 //给定一个字符串,请你找出其中不含有重复字符的 最长子串 ...

  10. 为什么用bower 安装bootstrap而不用npm来安装?

    NPM(node package manager),通常称为node包管理器.顾名思义,它的主要功能就是管理node包,包括:安装.卸载.更新.查看.搜索.发布等. npm的背后,是基于couchdb ...