Java中HashMap的实现原理
2017-08-28 16:27
363 查看
1. HashMap原理图
自我总结一句话: 当对象作为Key时, 只有两个对象hashcode相同, 而且equals,存放在HashMap中,key才算相同,其他情况都算key不同
先上图: 可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点
HashMap存放的原理: 先查看对象的hashcode是否相同, 如果不同, 存放在不同的散列表里(即不同的数组下标里),如果hashcode相同,就会比较两个对象是否equals,如果equals,覆盖之前的值, 如果不equals,存放在以该hashcode为头链表的单链表的下一个节点中
上面的图其实有点不好理解,真正的存放方式可以看下图所以
结构
上面代码对应的内存中的分配应该是
在数组中的下标位置需要按照以下方式计算出: 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. 举例
这里不做详细的说明了, 运行以下代码可以更好的理解
===========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相同, 才是相同的元素, 其他情况都是不同的元素===
自我总结一句话: 当对象作为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; } @Override public 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不一样,会产生不同的key public 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; } @Override public int hashCode() { if (name.length()%2== 0){ return 1; }else{ return 2; } } @Override public 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一样就是同一个元素 * b67a * */ public class HashMapKey3 { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int hashCode() { if (name.length()%2== 0){ return 1; }else{ return 2; } } @Override public 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 stub System.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相同, 才是相同的元素, 其他情况都是不同的元素===
相关文章推荐
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- Java集合HashMap的实现原理(借鉴)
- 深入Java集合学习系列:HashMap的实现原理
- (转)java学习:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- Core Java --集合--HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- Java HashMap实现原理
- java集合框架学习—HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理