package com.emsn.crazyjdk.java.util;  

 /**
* “人”类,重写了equals和hashcode方法...,以id来区分不同的人,你懂的...
*
* @author emsn1026
*
*/ public class Person { /**
* 身份id
*/
private String id; /**
* 姓名
*/
private String name; public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.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;
Person other = (Person) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
} @Override
public String toString() {
return "Person [id=" + id + ", name=" + name + "]";
} }

新建一个Person类,重写其中的equals和hashcode方法。这样,同样id的人会被认为是同样的事例,不同id的即时姓名相同也是不同的人,把Person类的实例作为HashMap的key时,key的唯一性讲通过Person实例的id来控制.

package com.emsn.crazyjdk.java.util;  

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry; import com.emsn.crazyjdk.java.util.Person; /**
* @author emsn1026
*
*/
public class MapTest { /**
* @param args
*/
public static void main(String[] args) {
Map m = new HashMap();
Person p1 = new Person();
Person p2 = new Person(); p1.setId("1");
p1.setName("name1");
p2.setId("1");
p2.setName("name2"); m.put(p1, "person1");
m.put(p2, "person2"); System.out.println("Map m's size :" + m.size()); for(Object o :m.entrySet()){
Entry e = (Entry)o;
System.out.println("key:"+ e.getKey());
System.out.println("value:"+ e.getValue());
} } }

打印的结果是 
Map m's size :1 
key:Person [id=1, name=name1] 
value:person2

可见key已存在,value被覆盖,这个结果可以预测。那么接下来我们把代码修改下:

package com.emsn.crazyjdk.java.util;  

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry; import com.emsn.crazyjdk.java.util.Person; /**
* @author emsn1026
*
*/
public class MapTest { /**
* @param args
*/
public static void main(String[] args) {
Map m = new HashMap();
Person p1 = new Person();
Person p2 = new Person(); p1.setId("1");
p1.setName("name1");
p2.setId("2");
p2.setName("name2"); m.put(p1, "person1");
m.put(p2, "person2"); System.out.println("Map m's size :" + m.size()); p2.setId("1"); System.out.println("Map m's size :" + m.size()); for(Object o :m.entrySet()){
Entry e = (Entry)o;
System.out.println("key:"+ e.getKey());
System.out.println("value:"+ e.getValue());
} } }

此处的变化是将p1,p2的id设成不同,然后都作为key插入map,因为两个key不相同,所以我们的预测是都可以插入,此时map的size应该为2,待插入后我们修改p2的id为1,即与p1相同,这样就造成了两个entry的key相同的情况,测试再查看map的结构,看看是不是还是刚才插入的两项。 
    此时我们不知道HashMap的内部实现,所以我们不知道它的实例会不会在数据插入后还继续维持key的唯一性。 
    我们可以猜测的是三种答案: 
    1.抛出异常,不允许修改p2的id与p1相同,维护key的唯一性; 
    2.可以修改,但根据某种算法删除p1或p2中的一项,也能起到维护key的唯一性; 
    3.可以修改,没有任何事情发生....两项id相同的person实例并存于map中,即存在同一个key对应了两个value。

那么各位在没尝试并且没有查看过HashMap的源代码时会做出怎样的选择呢?

结果打印如下:

Map m's size :2 
key:Person [id=1, name=name2] 
value:person2 
key:Person [id=1, name=name1] 
value:person1

那么是预测的第三种情况...这原本不是我最看好的答案..这样我就有一个疑问了,既然可以有两个相同的key对应不同的value存在,那么我通过这个key应该拿到的value是哪个呢?在上述代码的main方法末尾加入以下两行代码:

 System.out.println("Map m 通过get方法用key p1:"+p1+"时,获取的value:"+m.get(p1));
System.out.println("Map m 通过get方法用key p2:"+p2+"时,获取的value:"+m.get(p2));

得到的结果如下:

Map m 通过get方法用key p1:Person [id=1, name=name1]时,获取的value:person1 
Map m 通过get方法用key p2:Person [id=1, name=name2]时,获取的value:person1

可见不论你使用p1还是p2,得到的value都是person1。

/**
*jdk中get方法的源码
* Returns the value to which the specified key is mapped in this identity
* hash map, or <tt>null</tt> if the map contains no mapping for this key.
* A return value of <tt>null</tt> does not <i>necessarily</i> indicate
* that the map contains no mapping for the key; it is also possible that
* the map explicitly maps the key to <tt>null</tt>. The
* <tt>containsKey</tt> method may be used to distinguish these two cases.
*
* @param key the key whose associated value is to be returned.
* @return the value to which this map maps the specified key, or
* <tt>null</tt> if the map contains no mapping for this key.
* @see #put(Object, Object)
*/
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;//--关键在这里。
}
return null;
}

如果一个Key对应2个Value。 他按顺序找到后,直接就 Return a.value了。而不会循环Person2.

【jdk源码学习】HashMap的更多相关文章

  1. 由JDK源码学习HashMap

    HashMap基于hash表的Map接口实现,它实现了Map接口中的所有操作.HashMap允许存储null键和null值.这是它与Hashtable的区别之一(另外一个区别是Hashtable是线程 ...

  2. 从JDK源码学习Hashmap

    这篇文章记录一下hashmap的学习过程,文章并没有涉及hashmap整个源码,只学习一些重要部分,如有表述错误还请在评论区指出~ 1.基本概念 Hashmap采用key算hash映射到具体的valu ...

  3. JDK源码学习笔记——LinkedHashMap

    HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序. LinkedHashMap保证了元素迭代的顺序.该迭代顺序可以是插入顺序或者是访问顺序.通过维护一个 ...

  4. JDK源码学习--String篇(二) 关于String采用final修饰的思考

    JDK源码学习String篇中,有一处错误,String类用final[不能被改变的]修饰,而我却写成静态的,感谢CTO-淼淼的指正. 风一样的码农提出的String为何采用final的设计,阅读JD ...

  5. JDK源码学习系列05----LinkedList

                                             JDK源码学习系列05----LinkedList 1.LinkedList简介 LinkedList是基于双向链表实 ...

  6. JDK源码学习系列04----ArrayList

                                                                             JDK源码学习系列04----ArrayList 1. ...

  7. JDK源码学习系列03----StringBuffer+StringBuilder

                         JDK源码学习系列03----StringBuffer+StringBuilder 由于前面学习了StringBuffer和StringBuilder的父类A ...

  8. JDK源码学习系列02----AbstractStringBuilder

     JDK源码学习系列02----AbstractStringBuilder 因为看StringBuffer 和 StringBuilder 的源码时发现两者都继承了AbstractStringBuil ...

  9. JDK源码学习系列01----String

                                                     JDK源码学习系列01----String 写在最前面: 这是我JDK源码学习系列的第一篇博文,我知道 ...

  10. 【JDK】JDK源码分析-HashMap(2)

    前文「JDK源码分析-HashMap(1)」分析了 HashMap 的内部结构和主要方法的实现原理.但是,面试中通常还会问到很多其他的问题,本文简要分析下常见的一些问题. 这里再贴一下 HashMap ...

随机推荐

  1. Apache HttpComponents 获取Cookie

    package org.apache.http.examples.client; import java.util.List; import org.apache.http.HttpEntity; i ...

  2. ubuntu rails3.2.16 提示gem mysql adapter

    把database.yml的adapter改为mysql2 把Gemfile文件中的gem mysql改为gem mysql2

  3. struts2中struts.xml和web.xml文件解析及工作原理

    web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp ...

  4. 【转载】C#进阶系列——动态Lamada(二:优化)

    前言:前几天写了一篇动态Lamada的文章C#进阶系列——动态Lamada,受园友xiao99的启发,今天打算来重新优化下这个动态Lamada的工具类.在此做个笔记,以免以后忘了. 一.原理分析 上篇 ...

  5. 【转】记一次ASP.NET MVC性能优化(实际项目中)

    前言 在开发中为了紧赶项目进度而未去关注性能的问题,在项目逐渐稳定下来后发现性能令人感到有点忧伤,于是开始去关注这方面,本篇为记录在开发中遇到的问题并解决,不喜勿喷.注意:以下问题都是在移动端上出现, ...

  6. 转载:30多条mysql数据库优化方法,千万级数据库记录查询轻松解决

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...

  7. QTableView修改数据后弹出是否保存的提示框。

    自定义CustomDelegate继承自QStyledItemDelegate,重写setModelData(self, editor, model, index)方法 def setModelDat ...

  8. centos6.5的软件安装,用户管理等

    1.软件的安装命令: 第一种:以 软件名.rpm 为结尾的二进制文件,这样的文件的安装 rpm -ivh 软件名.rpm i 是 install v 是校验 h 进度 卸载软件 是 rpm -e 软件 ...

  9. 一直误解的memset函数

    1.“想当然”导致的后果 今天犯了一个十分低级的错误,在对一个整型数组用memset进行初始化设置所有元素值为1.可是结果却大出所料,很意外啊!接着,我就做了代码测试. #include <io ...

  10. CentOS6.8设置开机直接进入命令行模式

    在linux图形界面中,按 “Alt+Ctrl+F1”,进入命令行界面. 切换到root用户 # vi /etc/inittab 找到下面的语句: # Default runlevel. The ru ...