任何复杂的知识往往给人的感觉都是那么的不友好,所以,一定要静下心来,耐心去看、去读、去理解这些东西!!!
1.Set接口的简介
说明:Set 接口和 List 接口一样,同样继承自 Collection 接口。
特点:Set 接口中的元素无序,并且都会以某种规则保证存入的元素不出现重复。
2.HashSet类简介
说明:HashSet 是 Set 接口的一个实现类,它所存储的元素不可重复,并且无序。
特点:当向 HashSet 集合中添加一个元素时,首先会调用该元素的 hashCode() 方法来确定元素的存储位置,然后再调用元素对象的 equals() 方法来确保该位置没有重复元素。
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("---------------------------------------------");
}
}
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);
}
}
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);
}
}
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);
}
}
可以看到,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);
}
}
可以看到,输出结果中,hashset 集合的元素并没有重复,因此,如果我们想要用Hashset 集合存储自定义类型的数据,一定要记得覆盖重写 hashCode() 方法和 equals() 方法。