任何复杂的知识往往给人的感觉都是那么的不友好,所以,一定要静下心来,耐心去看、去读、去理解这些东西!!!  

1.Set接口的简介

说明:Set 接口和 List 接口一样,同样继承自 Collection 接口。

特点:Set 接口中的元素无序,并且都会以某种规则保证存入的元素不出现重复。

Java——集合中的Set接口通过HashSet类实现一些常用的方法_java

 

2.HashSet类简介 

说明:HashSet 是 Set 接口的一个实现类,它所存储的元素不可重复,并且无序。

特点:当向 HashSet 集合中添加一个元素时,首先会调用该元素的 hashCode() 方法来确定元素的存储位置,然后再调用元素对象的 equals() 方法来确保该位置没有重复元素。

Java——集合中的Set接口通过HashSet类实现一些常用的方法_java_02

 

3.HashSet类的常用方法

import java.util.*;
/*import java.util.HashSet;
  import java.util.Iterator;*/
public class HashSetDemo {
	public static void main(String[] args) {
		HashSet<String> hash=new HashSet<String>();
		System.out.println("↓↓↓HashSet集合常用方法如下↓↓↓");
		System.out.println("---------------------------------------------");
		System.out.println("创建第一个空的HashSet集合,元素类型为String类:" + hash);
		System.out.println("hash集合此时为空:" + hash.isEmpty());
		System.out.println("---------------------------------------------");
		hash.add("A");
		hash.add("B");
		hash.add("C");
		hash.add("D");
		hash.add("A");//元素无序,不能重复
		System.out.println("向hash集合中添加指定元素:" + hash);
		System.out.println("此时hash集合的长度为:" + hash.size());
		System.out.println("---------------------------------------------");
		System.out.print("使用迭代器遍历hash集合:");
		//第一步:使用集合中的iterator()方法,获取送代器的实现类对象
                //第二步:使用Iterator接口接收(多态)
		Iterator iterator=hash.iterator();
		while(iterator.hasNext()) {
			/*第三步:使用Iterator接口中的hasNext()方法来判断集合中还有没有下一个元素
			  如果有下一个元素,那么就调用Iterator接口的next()方法,取出下一个元素,并且会把指针向后移一位
			  第四步:使用Iterator接口中的next()方法来取出集合中的下一个元素
			  以此类推,直到hasNext()方法返回false,表示到达了集合的末尾终止对元素的遍历*/
			System.out.print(iterator.next() + " ");
		}
		System.out.println();
		System.out.println("---------------------------------------------");
		System.out.println("使用for-each循环遍历hash集合,同时获取每个元素和哈希值:");
		for(String a : hash) {
			System.out.print(a + " " + a.hashCode() + ",");
		}
		System.out.println();
		System.out.println("---------------------------------------------");
		System.out.println("hash集合中是否包含元素\"A\":" + hash.contains("A"));
		System.out.println("---------------------------------------------");
		hash.remove("D");
		System.out.println("移除元素\"D\"之后的hash集合为:" + hash);
		System.out.println("---------------------------------------------");
		hash.clear();
		System.out.println("清空hash集合中的所有元素:" + hash);
		System.out.println("---------------------------------------------");
	}
}

Java——集合中的Set接口通过HashSet类实现一些常用的方法_System_03

 

4.在Java程序中的哈希值 

Java 中的哈希值:是一个十进制的整数,由系统随机给出的二进制数经过换算得到的。其实它就是对象的地址值,是一个逻辑地址,是模拟出来得到地址,并不是数据实际存储的物理地址。

在Object类有一个方法,可以获取对象的哈希值:int hashCode() 返回对象的哈希码值。
hashCode()方法源码:public native int hashCode();  native:代表该方法是调用本地操作系统的方法。

下面,我们举个例子来看一下:👇👇👇

class Person extends Object {
//Java中的所有类都默认继承了Object类,这里不写extends Object也可以
	private String name;
	private int age;
	public Person() {	
	}
	public Person(String name,int age) {
		this.name=name;
		this.age=age;
	}
	public String getName() {
		return this.name;
	}
	public int getAge() {
		return this.age;
	}
}
public class HashCode {
	public static void main(String[] args) {
		Person p1=new Person("张三",20);//通过构造方法创建两个类对象
		Person p2=new Person("李四",22);
		int x1=p1.hashCode();//调用Object类的hashCode方法,获取哈希值,p1的哈希值是不变的
		System.out.println("对象p1的哈希值为:" + x1);
		int x2=p2.hashCode();//调用Object的hashCode方法,获取哈希值,p2的哈希值也是不变的
		System.out.println("对象p2的哈希值为:" + x2);
	}
}

Java——集合中的Set接口通过HashSet类实现一些常用的方法_bc_04

4.1 假如覆盖重写hashCode方法,所创建的对象的哈希值就会被影响 

class Person extends Object {
//Java中的所有类都默认继承了Object类,这里不写extends Object也可以
	public int hashCode() {
		return 999;
	}
}
public class HashCode {
	public static void main(String[] args) {
		Person p1=new Person();//通过构造方法创建两个类对象
		Person p2=new Person();
		int x1=p1.hashCode();//调用Object类的hashCode方法,获取哈希值,p1的哈希值是不变的
		System.out.println("对象p1的哈希值为:" + x1);
		int x2=p2.hashCode();//调用Object的hashCode方法,获取哈希值,p2的哈希值也是不变的
		System.out.println("对象p2的哈希值为:" + x2);
	}
}
//此时对象p1和p2的哈希值为:999

例如,我们常用的处理字符串的 String 类,它也覆盖重写了 hashCode 方法。

 

5.Set集合存储元素不重复的原理 

5.1 概念理解 

set 集合在调用 add() 方法的时候,add() 方法会调用元素的 hashCode() 方法和 equals() 方法判断元素是否重复。

5.2 应用举例 

import java.util.*;
public class HashCode {
	public static void main(String[] args) {
		HashSet<String> hashset=new HashSet<String>();
		String s1=new String("abc");
		String s2=new String("abc");
		hashset.add(s1);
		hashset.add(s2);
		hashset.add("LOL");
		hashset.add("DNF");
		System.out.println("s1的哈希值:" + s1.hashCode());
                System.out.println("s2的哈希值:" + s2.hashCode());
                System.out.println("LOL的哈希值:" + "LOL".hashCode());
                System.out.println("DNF的哈希值:" + "DNF".hashCode());
                System.out.println("HashSet集合:" + hashset);
	}
}

Java——集合中的Set接口通过HashSet类实现一些常用的方法_System_05

5.3 代码讲解 

最初,hashset集合是空的

hashset.add(s1)的时候,

第一步:add()方法首先会调用 s1的 hashCode()方法,计算字符串"abc"的哈希值,其哈希值是 96354,

第二步:查找集合中哈希值是 96354中的元素,没有发现哈希值是96354的 key

第三步:将s1存储到集合hashset中(于是集合hashset中存在哈希值 96354,且对应这数据 s1)

hashset.add(s2)的时候

第一步:add()方法首先会调用 s2的 hashCode()方法,计算字符串"abc"的哈希值,其哈希值是96354,

第二步:查找集合hashset中是否存在哈希值是 96354,即哈希值 96354冲突,

第三步:s2调用 equals()方法,和集合中哈希值是 96354对应的元素进行比较

第四步:s2.equals(s1)返回 true,即哈希值是 96354对应的元素已经存在,所以就不添加 s2进集合了(其中:s1 = "abc",s2 = "abc")

hashset.add("LOL")的时候

第一步:调用 "LOL" 的 hashCode()方法,计算字符串 "LOL" 的哈希值,其哈希值是640503,

第二步:查找集合中哈希值是 640503中的元素,没有发现哈希值是 640503的 key,

第三步:将 "LOL" 存储到集合hashset中(于是集合hashSet中存在哈希值 640503,且对应这数据 "LOL")

hashset.add("DNF")的时候

第一步:调用 "DNF" 的 hashCode()方法,计算字符串 "DNF" 的哈希值,其哈希值是644843,

第二步:查找集合中哈希值是 644843中的元素,没有发现哈希值是644843的 key,

第三步:将 "DNF" 存储到集合hashSet中(于是集合hashset中存在哈希值644843,且对应这数据 "DNF")

添加完成,集合hashset = [abc, LOL, DNF]

 

6.HashSet存储自定义类型元素 

HashSet 存储自定义类型元素,那么自定义的类必须重写 hashCode() 方法和 equals() 方法,否则添加的元素可以出现重复,我们平时使用的类型,它们都重写 Object 类的 hashCode() 方法和 equals() 方法。

6.1 假如不重写Object类的hashCode()方法和equals()方法

import java.util.*;
class Person extends Object {
//Java中的所有类都默认继承了Object类,这里不写extends Object也可以
	private String name;
	private int age;
	public Person() {	
	}
	public Person(String name,int age) {
		this.name=name;
		this.age=age;
	}
	public String getName() {
		return this.name;
	}
	public int getAge() {
		return this.age;
	}
	public String toString() {
		return "Person{" + "name=\"" + name + "\"" + ",age=" + age + "}";
	}
}
public class HashCode {
	public static void main(String[] args) {
		HashSet<Person> hashset=new HashSet<Person>();
		Person p1=new Person("小明",20);
		Person p2=new Person("小明",20);
		Person p3=new Person("小红",25);
		hashset.add(p1);
		hashset.add(p2);
		hashset.add(p3);
		System.out.println("创建的hashset集合为:" + hashset);
	}
}

Java——集合中的Set接口通过HashSet类实现一些常用的方法_bc_06

可以看到,hashset 集合里面可以存在重复的元素。

6.2 重写Object类的hashCode()方法和equals()方法

import java.util.*;
class Person extends Object {
//Java中的所有类都默认继承了Object类,这里不写extends Object也可以
	private String name;
	private int age;
	public Person() {	
	}
	public Person(String name,int age) {
		this.name=name;
		this.age=age;
	}
	public String getName() {
		return this.name;
	}
	public int getAge() {
		return this.age;
	}
	public String toString() {
		return "Person{" + "name=\"" + name + "\"" + ",age=" + age + "}";
	}
	public boolean equals(Object o) {
        if(this==o) {//参数==对象
            return true;
        }
        if(o==null || getClass()!=o.getClass()) {//传入参数为空,或者对象与参数的hashCode不相等
            return false;
        }
        Person person=(Person) o;//向下转型,把Object类型转型为Person类型
        return age==person.age && Objects.equals(name,person.name);//返回 age,name
    }
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
public class HashCode {
	public static void main(String[] args) {
		HashSet<Person> hashset=new HashSet<Person>();
		Person p1=new Person("小明",20);
		Person p2=new Person("小明",20);
		Person p3=new Person("小红",25);
		hashset.add(p1);
		hashset.add(p2);
		hashset.add(p3);
		System.out.println("创建的hashset集合为:" + hashset);
	}
}

Java——集合中的Set接口通过HashSet类实现一些常用的方法_System_07

可以看到,输出结果中,hashset 集合的元素并没有重复,因此,如果我们想要用Hashset 集合存储自定义类型的数据,一定要记得覆盖重写 hashCode() 方法和 equals() 方法。