Set集合的特点
Set系列集合特点:无序:添加数据的顺序和获取出的数据顺序不一致;不重复;无索引;
- HashSet:无序、不重复、无索引。
- LinkedHashSet:有序、不重复、无索引。
- TreeSet:排序、不重复、无索引。
import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import java.util.TreeSet; public class SetDemo1 { public static void main(String[] args){ //1.创建一个Set集合的对象 // Set<Integer> set = new HashSet<>(); //创建了一个HashSet的集合对象 经典代码 HashSet:无序不重复无索引 // Set<Integer> set = new LinkedHashSet<>(); //有序 不重复 无索引 Set<Integer> set = new TreeSet<>(); set.add(666); set.add(555); set.add(555); set.add(888); set.add(888); set.add(777); set.add(777); System.out.println(set); } }
运行结果:
注意:
Set要用到的常用方法,基本上就是collection提供的。
自己几乎没有额外新增一些常用功能。
HashSet集合的底层原理
哈希值
- 就是一个int类型的数值,Java中每个对象都有一个哈希值。
- Java中的所有对象,都可以调用Obejct类提供的hashcode方法,返回该对象自己的哈希值。
public int hashCode():返回对象的哈希码值。
对象哈希值的特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的。
- 不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。
(int的范围:-21亿多~21亿多)
我们用代码演示一下
先定义一个学生类:
public class SetDemo2 { public static void main(String[] args) { /* Java中的所有对象,都可以调用Object类提供的hashCode方法,返回该对象自己的哈希值 public int hashCode(): 返回对象的哈希值. 同一个对象多次调用hashCode()方法返回的哈希值是相同的. 不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞) */ Student s1 = new Student("蜘蛛精",25,169.5); Student s2 = new Student("紫霞",22,166.5); System.out.println("第一次取s1的哈希值: " + s1.hashCode()); System.out.println("第二次取s1的哈希值: " + s1.hashCode()); System.out.println("第一次取s2的哈希值: " + s2.hashCode()); System.out.println("第二次取s2的哈希值: " + s2.hashCode()); System.out.println(); //哈希碰撞 String str1 = new String("abc"); String str2 = new String("acD"); System.out.println("str1的哈希值: " + str1.hashCode()); System.out.println("str2的哈希值: " + str2.hashCode()); } }
运行结果:
HashSet集合的底层原理
- 基于哈希表实现。
- 哈希表是一种增删查改的数据性能都较好的数据结构。
哈希表
JDK8之前,哈希表=数组+链表
JDK8开始,哈希表=数组+链表+红黑树
JDK8之前HashSet集合的底层原理,基于哈希表:数组+链表
- 创建一个默认长度16的数组,默认加载因子为0.75,数组名table
- 使用元素的哈希值对数组的长度求余计算出应存入的位置
- 判断当前位置是否为null,如果是null直接存入
- 如果不为null,表示有元素,则调用equals方法比较。相等,则不存;不相等,则存入数组
JDK8之前,新元素存入数组,占老元素位置,老元素挂下面(也就是形成一个链表)
JDK8开始HashSet集合的底层原理,基于哈希表:数组+链表+红黑树
- JDK8开始后,哈希表中引入了红黑树,进一步提高了操作数据的性能。
当链表长度超过8,且数组长度>=64时,自动将链表转成红黑树
去重复原理
HashSet集合默认不能对内容一样的两个不同对象去重复。
比如内容一样的两个学生对象存入到HashSet集合中去,HashSet集合是不能去重复的。
如何让HashSet集合能够实现对内容一样的两个不同对象也能去重复?
先了解一下去重复机制的原理:
- HashSet首先会根据hashCode()方法计算出的哈希值找到元素应该存放的位置。
- 位置中如果没有元素则直接存放,若已有元素,则再使用equals()方法对该位置的元素一一进行比较。
- 当equals()方法返回true时就不进行存储,从而起到了去重复的效果。
所以,得到结论:
- 如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法。
实例演示
import java.util.HashSet; import java.util.Set; public class SetDemo3 { public static void main(String[] args) { Set<Student> students = new HashSet<>(); Student s1 = new Student("至尊宝",28,169.6); Student s2 = new Student("蜘蛛精",23,169.6); Student s3 = new Student("蜘蛛精",23,169.6); System.out.println("s2的哈希值: " + s2.hashCode()); System.out.println("s3的哈希值: " + s3.hashCode()); Student s4 = new Student("牛魔王",48,169.6); students.add(s1); students.add(s2); students.add(s3); students.add(s4); System.out.println(students); } }
先看主程序,没有重写对象hashCode()方法和equals()方法的运行结果:
重写对象hashCode()方法和equals()方法
import java.util.Objects; public class Student { private String name; private int age; private double height; public Student() { } public Student(String name, int age, double height) { this.name = name; this.age = age; this.height = height; } 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 double getHeight() { return height; } public void setHeight(double height) { this.height = height; } @Override public String toString() { return '\n' + "名字:" + name + '\n' + "年龄:" + age + '\n' + "身高:" + height + '\n'; } //只要两个对象内容一样,就返回true @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && Double.compare(student.height, height) == 0 && Objects.equals(name, student.name); } //只要两个对象内容一样,返回的哈希值就一样 @Override public int hashCode() { //根据名字 身高 年龄来计算哈希值 return Objects.hash(name, age, height); } }
重写完之后我们就可以重新看一下运行结果:
可以看到,返回的哈希值是一样的,并且内容重复的两个不同对象也没有都加入到集合中。
END