HashMap中的重写enquls 和 hashCode 有什么用?

作为一个入门级的程序员 , HashMap 我们肯定都用过,但是我们是否真正了解过它呢? 那么看了这篇文章,看看是否让你对HashMap有新的认识。

 

首先HashMap基本的用法大家肯定都熟悉,像下面这样:

 

	Map testMap1=new HashMap<String,String>();
		testMap1.put("Mrwang", "我是一个垃圾");
		testMap1.put("Mrliu", "我是一个菜鸟");

那它内部是怎么存储的呢?下面我们来探究其背后的原理:

简单来说,HashMap是数组和链表的结合(这里先不考虑java8的优化), ArrayList 和 LinkList大家都熟悉,他们就是数组和链表的代表,HashMap 结合了两者。它的数据结构如下,水平方向是一个数组,垂直方向是个链表。

 

 

插入操作:

首先获取对象的hashCode, 通过%数组长度得到对象所要插入的数组下标(这个过程也叫散列),然后在垂直方向上遍历链表,如果有重复对象,就将其替换,如果没有,就向下移动,最后插入队列尾部。

获取操作:

查找方法同插入相同,首先计算hashCode, 然后通过模运算找到数组所在下标,然后遍历链表查找。

 

两个步骤都说到了一个比较两个对象是否相等,那么hashMap中如何判断两个对象是一个对象,并将其替换的呢?

不着急,我们先来看一个例子:

public static void main(String[] args) {
		// TODO Auto-generated method stub

		Map testMap=new HashMap<Object,String>();
		Person p1=new Person();
		p1.setName("a");
		p1.setAge(22);
		p1.setId("id1");
		Person p2=new Person();
		p2.setName("a");
		p2.setAge(22);
		p2.setId("id1");
		testMap.put(p1, "我是第一个人");
		testMap.put(p2, "我是第二个人");
		Iterator iterator=testMap.entrySet().iterator();
		while(iterator.hasNext()) {
			Map.Entry entry=(Map.Entry)iterator.next();
		    Person p=(Person)entry.getKey();
		    String value=(String)entry.getValue();
		    System.out.println("person:"+p.toString()+",value:"+value);
		}
}

我们插入两个对象到HashMap中 ,注意这里的key不是基础类型,而是我们自定义类型Person, 看一下它的输出:

person:name->a,age->22,id->id1,value:我是第一个人
person:name->a,age->22,id->id1,value:我是第二个人

两个对象的值都一样,但是都能put成功,说明HashMap没有将其认为是一个对象,这是为什么呢?

这里先来了解一下HashMap 判断Key是否存在的规则:

1.首先计算key的hashCode, 如果hashCode在hashMap中不存在,直接插入。

2.如果hashCode 已存在,那么找出所有hashCode相同的key,分别调用key的equals方法判断当前的key是否与其相同。如果equals()返回true,则说明需要添加的key已经存在,那么hashMap会用新的value替换掉旧的value的值; 如果equals()都返回false,则说明没有相同的key存在,则在hashMap中建立新的映射关系。

所以我们找到原因了,肯定是两个Person对象在HashMap中比较中要么就是HashCode() 比较不同,要么就是equals()比较返回false。我们依次来看。

我们没有重写Person的hashCode和equals方法,所以所调用的都是它的父类的,也就是Object的hashCode和equals方法,下面依次来看:

先看Object的hashCode:

public native int hashCode();

 是个native方法,这里就不追了,注释上有这么一段话:

     * As much as is reasonably practical, the hashCode method defined by
     * class {@code Object} does return distinct integers for distinct
     * objects. (This is typically implemented by converting the internal
     * address of the object into an integer, but this implementation
     * technique is not required by the
     * Java&trade; programming language.)

我们可以理解为是hashCode() 返回的是对象的内存地址转化为的整数。

上例中p1和p2是两个对象,内存地址不同,hashCode() 不相同。

再看Object的equals():

   public boolean equals(Object obj) {
        return (this == obj);
    }

好像也是比较的是内存地址,Object对象只有当两个对象是同一对象的时候,才会返回true,否则返回false.

结合上例,在hashMap的比较中equals() 也不会比较通过(当然hashCode不同不会走到这一步)

 

回到上例,我们如果想让属性相同的Person对象在HashMap中只保存一个,怎么来实现呢,很简单,就是重写hashCode和equals:

package com.example.hashmaptest;

public class Person {

	public static final Object HashObject=new Object();
	String name;
	int age;
	String id;

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "name->"+name+",age->"+age+",id->"+id;
	}

	@Override
	public boolean equals(Object obj) {
		// TODO Auto-generated method stub
		if(obj instanceof Person) {
			Person p=(Person)obj;
			if(p.age==this.age
					&&p.id==this.id
					&&p.name==this.name) {
				return true;
			}
		}
		return false;
	}

	@Override
	public int hashCode() {
		// TODO Auto-generated method stub
		return HashObject.hashCode();
	}

}

如果理解了上面的原理,这个类的equals和hashCode方法的重写应该很简单了吧。如果以后遇到人问到你hashMap的问题,是不是不会迷茫了呢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龍林1102

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值