Java中HashMap的实现原理

来源:互联网 发布:英雄联盟黑号淘宝 编辑:程序博客网 时间:2024/04/23 17:44

1. HashMap原理图

自我总结一句话: 当对象作为Key时, 只有两个对象hashcode相同, 而且equals,存放在HashMap中,key才算相同,其他情况都算key不同


先上图: 可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点


HashMap存放的原理: 先查看对象的hashcode是否相同, 如果不同, 存放在不同的散列表里(即不同的数组下标里),如果hashcode相同,就会比较两个对象是否equals,如果equals,覆盖之前的值, 如果不equals,存放在以该hashcode为头链表的单链表的下一个节点中

上面的图其实有点不好理解,真正的存放方式可以看下图所以

结构

HashMap<String, Integer> map = new HashMap<String, Integer>();map.put("语文", 1);map.put("数学", 2);map.put("英语", 3);map.put("历史", 4);map.put("政治", 5);map.put("地理", 6);map.put("生物", 7);map.put("化学", 8);for(Entry<String, Integer> entry : lmap.entrySet()) {    System.out.println(entry.getKey() + ": " + entry.getValue());}

上面代码对应的内存中的分配应该是



在数组中的下标位置需要按照以下方式计算出: hash(key)%数组长度    828410%16=10( 0 就是下标0  10 就是下标10 ,并不是第10个元素)

2. HashMap 源码中主要属性


3. HashMap图解以及源码的联系


 其中Node<K,v>的内部结构为


详细结构见下图绿色部分


entrySet  table图解


4. HasMap中对HashCode碰撞的解决方法:

hashcode碰撞,意思为HashCode一样, 这种情况,就是使用拉链法(即单链表)解决, Java1.8以后如果链表长度大于8,链表将转换为红黑树

5. Java中重写Equals方法, 需要同时重写hashcode的原因:

就是为了满足Java 中哈希的使用,比如HashMap, HashSet等的时候, 如果只重写了equlas方法,不重写hashcode方法,那默认的两个对象的hashcode值不同,如果将此对象作为HashMap的key,那就会put两个元素,而不会覆盖之前的元素。其他集合也是一样的道理。

6. 在HashMap中有两个很重要的参数,容量(Capacity)和负载因子(Load factor)

bucket即数组,Capacity就是bucket的大小,Load factor就是bucket填满程度的最大比例。如果对迭代性能要求很高的话不要把capacity设置过大,也不要把load factor设置过小。当bucket中的entries的数目大于capacity*load factor时就需要调整bucket的大小(即调用resize()方法)为当前的2倍。

HashMap 默认Capacity为16, Load factor 为0.75,这两个值可以通过HashMap的构造函数来修改默认大小


7. 举例

这里不做详细的说明了, 运行以下代码可以更好的理解

/** *hashcode一样,但不equal.就是不同的元素, 不重写equals,说明两个对象肯定不equals * @author * */public class HashMapKey {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic int hashCode() {if (name.length()%2== 0){return 1;}else{return 18;}}}

/** * 两个对象equals,但是hashcode 不相同的情况 , 不重写hashcode, 默认量对象hashcode不相同 * * */public class HashMapKey1 {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}@Override//重写了equals方法,没有重写HashCode, 认为两个对象equals,但是HashCode不一样,会产生不同的keypublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;HashMapKey1 other = (HashMapKey1) obj;if (name == null) {if (other.getName() != null)return false;} else if (name == (other.getName())){return true;}return false;}}

/** *hashcode一样,但不equal.就是不同的元素;两个对象equals,hashcode一样就是同一个元素 *  * */public class HashMapKey2 {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic int hashCode() {if (name.length()%2== 0){return 1;}else{return 2;}}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;HashMapKey2 other = (HashMapKey2) obj;if (name == null) {if (other.getName() != null)return false;} else if ((name.length()) == (other.getName().length())){return true;}return false;}}
/** * 两个对象equals,hashcode一样就是同一个元素 * * */public class HashMapKey3 {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic int hashCode() {if (name.length()%2== 0){return 1;}else{return 2;}}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;HashMapKey3 other = (HashMapKey3) obj;if (name == null) {if (other.getName() != null)return false;} else if ((name.length()%2) == (other.getName().length()%2)){return true;}return false;}}

public class HashMapPutTest {public static void testHashCode(){Map<HashMapKey,String> mapTest = new HashMap<HashMapKey, String>();HashMapKey hashMapKey1 = new HashMapKey();hashMapKey1.setName("1");HashMapKey hashMapKey2 = new HashMapKey();hashMapKey2.setName("12");HashMapKey hashMapKey3 = new HashMapKey();hashMapKey3.setName("123");HashMapKey hashMapKey4 = new HashMapKey();hashMapKey4.setName("1234");HashMapKey hashMapKey5 = new HashMapKey();hashMapKey5.setName("4567");mapTest.put(hashMapKey1, "1");mapTest.put(hashMapKey2, "12");mapTest.put(hashMapKey3, "123");mapTest.put(hashMapKey4, "1234");mapTest.put(hashMapKey5, "4567");System.out.println("size" + mapTest.size());for(HashMapKey key:mapTest.keySet()){System.out.println(key.getName());}for(Entry<HashMapKey, String> entry: mapTest.entrySet()){System.out.println(entry.getKey().getName() + ":" + entry.getValue());}}public static void testEqual(){Map<HashMapKey1,String> mapTest = new HashMap<HashMapKey1, String>();HashMapKey1 hashMapKey1 = new HashMapKey1();hashMapKey1.setName("1");HashMapKey1 hashMapKey2 = new HashMapKey1();hashMapKey2.setName("12");HashMapKey1 hashMapKey3 = new HashMapKey1();hashMapKey3.setName("123");HashMapKey1 hashMapKey4 = new HashMapKey1();hashMapKey4.setName("1234");HashMapKey1 hashMapKey5 = new HashMapKey1();hashMapKey5.setName("4567");mapTest.put(hashMapKey1, "1");mapTest.put(hashMapKey2, "12");mapTest.put(hashMapKey3, "123");mapTest.put(hashMapKey4, "1234");mapTest.put(hashMapKey5, "4567");System.out.println("size" + mapTest.size());for(Entry<HashMapKey1, String> entry: mapTest.entrySet()){System.out.println(entry.getKey().getName() + ":" + entry.getValue());}}public static void testHashEqual(){Map<HashMapKey2,String> mapTest = new HashMap<HashMapKey2, String>();HashMapKey2 hashMapKey1 = new HashMapKey2();hashMapKey1.setName("1");HashMapKey2 hashMapKey2 = new HashMapKey2();hashMapKey2.setName("12");HashMapKey2 hashMapKey3 = new HashMapKey2();hashMapKey3.setName("123");HashMapKey2 hashMapKey4 = new HashMapKey2();hashMapKey4.setName("1234");HashMapKey2 hashMapKey5 = new HashMapKey2();hashMapKey5.setName("4567");mapTest.put(hashMapKey1, "1");mapTest.put(hashMapKey2, "12");mapTest.put(hashMapKey3, "123");mapTest.put(hashMapKey4, "1234");mapTest.put(hashMapKey5, "6789");System.out.println("size" + mapTest.size());for(Entry<HashMapKey2, String> entry: mapTest.entrySet()){System.out.println(entry.getKey().getName() + ":" + entry.getValue());}}public static void testHashEqual1(){Map<HashMapKey3,String> mapTest = new HashMap<HashMapKey3, String>();HashMapKey3 hashMapKey1 = new HashMapKey3();hashMapKey1.setName("1");HashMapKey3 hashMapKey2 = new HashMapKey3();hashMapKey2.setName("12");HashMapKey3 hashMapKey3 = new HashMapKey3();hashMapKey3.setName("123");HashMapKey3 hashMapKey4 = new HashMapKey3();hashMapKey4.setName("1234");HashMapKey3 hashMapKey5 = new HashMapKey3();hashMapKey5.setName("4567");mapTest.put(hashMapKey1, "1");mapTest.put(hashMapKey2, "12");mapTest.put(hashMapKey3, "123");mapTest.put(hashMapKey4, "1234");mapTest.put(hashMapKey5, "4567");System.out.println("size" + mapTest.size());for(Entry<HashMapKey3, String> entry: mapTest.entrySet()){System.out.println(entry.getKey().getName() + ":" + entry.getValue());}}public static void main(String[] args) {// TODO Auto-generated method stubSystem.out.println("===========testEqual: 存在两个对象equals,但是hashcode 不相同的情况, 就是不同的元素 =================");testEqual();System.out.println("===========testHashCode: 存在两个对象hashcode一样,但不equal,不是相同的元素=================");testHashCode();System.out.println("===========testHashEqual: hashcode一样,但不equal.就是不同的元素;两个对象equals,hashcode一样就是同一个元素=================");testHashEqual();System.out.println("===========testHashEqual1: 两个对象equals,hashcode一样就是同一个元素=================");testHashEqual1();System.out.println("===========总结:两个对象equals,而且hashcode相同, 才是相同的元素, 其他情况都是不同的元素=================");}}
结果

===========testEqual: 存在两个对象equals,但是hashcode 不相同的情况, 就是不同的元素 =================

size5

12:12

4567:4567

1234:1234

1:1

123:123

===========testHashCode: 存在两个对象hashcode一样,但不equal,不是相同的元素=================

size5

12:12

1234:1234

4567:4567

1:1

123:123

===========testHashEqual: hashcode一样,但不equal.就是不同的元素;两个对象equals,hashcode一样就是同一个元素========

size4

12:12

1234:6789

1:1

123:123

===========testHashEqual1: 两个对象equals,hashcode一样就是同一个元素=================

size2

12:4567

1:123

===========总结:两个对象equals,而且hashcode相同, 才是相同的元素, 其他情况都是不同的元素===

原创粉丝点击