Java课堂篇10_hash集合的一些小细节(hash冲突、集合删除元素的原理、hashCode属性、集合存放的对象增删操作需要设计重写hashCode)

写在前面


除了需要掌握集合的基本使用,还需要对Java底层了解的深一点,对我们用好这门语言更有利。

目录


hash集合的一些小细节

  • 集合存放的对象增删操作需要设计重写hashCode ,否则导致元素删除失败
  • 集合删除元素的原理、
  • hashCode属性、
  • hash冲突、

开始

一、集合存放的自定义对象增删操作需要设计重写hashCode ,否则导致元素删除失败

先看小栗子

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

public class TestHashCode {
    
    
	public static void main(String[] args) {
    
    
		
		
		testDelElemFromSet();
		System.out.println("==============");
		testDelElemFromList();
	}
	public static void testDelElemFromSet() {
    
    
		Collection mySet = new HashSet();
		
		//添加元素
		mySet.add("xiaosi");
		mySet.add(123);
		mySet.add(129);
		mySet.add(new TestHashCodeStudent("si",20));
		
		//输出mySet的内容
		System.out.println(mySet + " 长度为: "+mySet.size());
		
		//删除元素
		mySet.remove("xiaosi");
		mySet.remove(123);
		mySet.remove(129);
		mySet.remove(new TestHashCodeStudent("si",20));
		
		//输出mySet的内容
		System.out.println(mySet + " 删除元素之后的长度为: "+ mySet.size());
		
	}
	
	public static void testDelElemFromList() {
    
    
		Collection myList = new ArrayList();
		
		//添加元素
		myList.add("xiaosi");
		myList.add(123);
		myList.add(129);
		myList.add(new TestHashCodeStudent("si",20));
		
		//输出myList的内容
		System.out.println(myList + " 长度为: "+myList.size());
		
		//删除元素
		myList.remove("xiaosi");
		myList.remove(123);
		myList.remove(129);
		myList.remove(new TestHashCodeStudent("si",20));
		
		//输出myList的内容
		System.out.println(myList + " 删除元素之后的长度为: "+ myList.size());
		
	}


}

执行结果
在这里插入图片描述
可见,对于List和Set集合中存放的 对象元素,Integer 和 String 类型的对象能移除

但是 自定义对象 却不能移除
在这里插入图片描述

在自定义的类中 重写继承Object 的 hashCode 方法 和 equals方法

在这里插入图片描述

再次测试,发现都删除成功了
在这里插入图片描述

但是只重写equals方法呢?

发现List集合删除成功,set集合删除失败

而且List集合会调用equals方法,但是set集合在 没有 重写hashCoe的时候,不会调用equals方法
在这里插入图片描述

为什么 重写之后就删除成功了呢,我们需要知道 集合 删除元素的原理

二、集合删除元素的原理

在这里插入图片描述

删除元素的时候,参数 是一个 新的对象

拿着这个新的对象去 mySet集合中比对,相等就删除

集合中存储的都是对象,关键是如何比较两个对象相等?

  • hashSet集合中使用hashCode来 标识 对象、使用 hashCode + equals解决哈希冲突的问题
  • ArrayList集合中使用equals 标识 对象、

三、hashCode、equals

在这里插入图片描述
在这里插入图片描述

hashCode和equals都是继承自Object的,不重写的情况下

  • hashCode是一个 格式为 xxx@16进制地址
  • equals默认比较的是地址
    在这里插入图片描述

重写Object的hashCode,最后经hash算法 根据 该类可选定 的 成员属性 得到一个计算模板

每个对象根据自己的成员属性值 来计算出 自己的 hashCode值,标识这个对象
在这里插入图片描述

因此

结合前面

  1. 自定义对象两个方法都不重写—》都删除失败

  2. 自定义对象只重写equals方法—》List集合删除成功且调用了equals方法,Set集合删除失败且没调用equals方法

  3. 自定义对象 两个方法都重写了—》都删除成功

    • set集合在存储前,调用了一次hashCode,删除过程中,先调用了hashCode,hashCode发现相等之后(开始hashCode未重写的时候,两个对象的hashCode 一定不相等,即在内存中的位置一定不相等,故不会调用equals),又调用了equals,两者都相等,认为这两个对象 相等

得出集合删除数据是

  • List集合:调用重写之后的equals,比较自定义指定的成员属性值;未重写则调用Object的比较 两对象的地址

  • Set集合:依赖于 参数对象(具体点是和hashCode相关的对象属性值) 来和 集合中存在 的元素比对,
    先比较hashCode是否相等----》接着比较equals----》两者都相等,认为 两对象相等
    (这也就是对象两对象" == "比较的流程)

String 和 Integer 是同样的原理,只不过是 Oracle 已经写好了 对应的hashCode 个 equals方法

拓展

  • 对于字符串常量池的字符串"=="返回为true相等,因为一个对象的hashCode一定相等,而new String("abc")存储地址在一般的堆区,称为副本对象可以多个,计算出的hashCode不同,直接返回false
  • 对于-128-127之间的int值,可以进行自动拆箱和装箱, "=="返回为true相等,因为一个对象的hashCode一定相等,而越界的 会是像 new Integer(400) 存储地区在一般的堆区,计算出的hashCode不同,直接返 false

四、hash冲突

在这里插入图片描述
对于不同的对象,重写hashCode之后,hash算法得出来的hashCode 可能出现相等的情况

因为set集合元素具有 无序、不允许重复的特点

重写过hashCode方法之后,创建对象存入HashSet的集合,会先计算hashCode值,根据hashCode值确定存放的位置

如果两个对象的 hashCode相同,但是equals是不同的

(例如,hashCode有 姓 计算出来,equals 不仅包括姓,还包括名,现在需要存储 同姓不同名的两个对象)

4.1这时会 引发 哈希冲突

出现哈希冲突的时候,简单理解,通俗理解冲突的元素都会被放在一个 hash桶中

当我们删除元素的时候,不仅会根据hashCode属性,还需要根据equals 才能找到 待删除的 元素

因此,这就要求我们在设计 类的时候,为了避免哈希冲突而引起的 集合删除对象元素失败

需要我们精心设计hashCode和equals函数,提高程序的健壮性

猜你喜欢

转载自blog.csdn.net/qq_24654501/article/details/111025804