首先我们需要明确这个问题,是因为hashmap或者hashset我们需要往里面put元素的时候,不能有两个完全的相同的值进入容器内部,否则,我们需要从集合中取出元素或者删除元素的的时候,就可能会导致重复的元素重复取出。
所以在往集合中存入元素的时候,就会涉及到两个操作,检查当前需要存入元素的key值的hashcode是否在集合中存在,如果存在,则再进行对equals的比较,如果equals为false,则存入。如果hashcode不存在,则直接放入集合(这里这样设计的原因是因为equals的性能开销比较大,也是为了防止不同元素存在hash冲突的时候无法存入集合的情况产生)。
第二,我们需要知道重写两个方法的前后都分别做了什么
- hashcode(重写前):jvm通过对象的内存地址值,为对象分配一个随机数。
- hashcode(重写后):通过对象的内容值,分配一个随机数
- equals(重写前):比对两个对象的内存地址值,如果两个引用指向同一个对象,则才会返回true,否则返回false
- equals(重写后):比对两个对象的内容值,如果内容完全相同,则返回true,否则返回false
根据上面的描述,我们可以分出两种情况讨论
- 当重写hashcode,而不去重写equals方法的时候:
首先我们拿到需要存放的元素的key值,调用它自身的hashcode方法,此时hashcode已经重写,返回的是根据他内容值获取的一个随机数,一般情况下,如果hashcode与集合中的元素的key值的hashcode重复,有两种情况,第一,是真的需要存入的元素和集合中的元素值相等,例如需要存入person.name=“小明”,而恰好,集合中已经有个元素的key为person.name=“小明”,他们俩是重复的,不再去存放,但是可能有第二种可能,我们在代码中hashcode是随机给到的一个数值,两个完全不相同的元素是完全有可能产生相同值的,尽管这个概率或许特别小,这种情况,我们称之为哈希碰撞,为了使得元素正确的存放进集合,此时,会调用到没有重写的equals方法对两个元素进行内存地址值的比较,如果此时我放入两个内容值完全相同的元素,但是不同对象的时候,例如我new两个对象,两个对象里面person1.name=“小明”,person2.name=“小明”,这个时候,由于他们俩是完全不同的对象,地址值肯定不同,equals会返回false,导致集合中存入相同的元素。 - 当不重写hashcode,只重写equals方法的时候
首先,还是一样的流程,当需要将元素put进集合的时候,会首先调用hashcode方法来比对是否相同,此时获取的是当前元素内存地址值的随机数,如果此时有两个内容值完全相同,但是两个不同对象的时候,他会因为两个对象内存地址不同,而获取到不同的hashcode,因此直接将元素放入集合,这样就直接不会再走equals判断,即使重写了equals逻辑也不会再有任何作用。
总结:综上所述,当我们使用hashmap或者hashset这类集合存放对象的时候,一定要同时重写hashcode和equals方法,否则一定会出现存放重复元素的情况发生,一般来说,我们使用hashmap的时候,key可以设置为String类型的,因为String默认帮我们重写了hashcode和equals方法,并且String在创建的时候,不会再堆内存中占用空间,会在字符串常量池中寻找是否有这样的字符串,有的话直接返回,没有的话,创建直接再返回。