您的位置:首页 > 编程语言 > Java开发

Java中HashMap的实现原理

2017-08-28 16:27 363 查看
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;
}

@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相同, 才是相同的元素, 其他情况都是不同的元素===
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: