Java set集合概述
set系列集合概述
set系列集合特点
- 无序:存取顺序不一致
- 不重复:可以去除重复
- 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素
set集合实现类特点:
- HashSet:无序,不重复,无索引
- LinkedHashSet:有序,不重复,无索引
- TreeSet:排序,不重复,无索引
实例:
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
public class SetDemo1 {
public static void main(String[] args) {
//看看Set系列集合的特点 HashSet, LinkedHashSet, TreeSet
Set<String> sets = new HashSet<>(); //无序,不重复,无索引
sets.add("Python");
sets.add("java");
sets.add("MySql");
sets.add("java");
sets.add("HTML");
sets.add("MySql");
sets.add("SpringBoot");
sets.add("java");
sets.add("MySql");
System.out.println(sets);
Set<String> sets1 = new LinkedHashSet<>(); //有序,无索引,不重复
sets1.add("Python");
sets1.add("java");
sets1.add("MySql");
sets1.add("java");
sets1.add("HTML");
sets1.add("MySql");
sets1.add("SpringBoot");
sets1.add("java");
sets1.add("MySql");
System.out.println(sets1);
}
}
HashSet元素无序的底层原理:哈希表
HashSet集合底层采取哈希表存储的数据
- HashSet集合底层采取哈希表存储的数据
- 哈希表是一种对于增删改查性能都较好的结构
哈希表的组成
- JDK8之前,底层使用数组+链表组成
- JDK8开始之后,底层采用数组+链表+红黑树组成
哈希值
- 是JDK根据对象的地址,按照某种规则算出来的int类型的数值
Object类的API
- public int hashCode(): 返回对象的哈希值
对象的哈希值特点
- 同一个对象多次调用hashCode方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的
public class SetDemo2 {
public static void main(String[] args) {
//学会获取对象的Hash值
String str = "fgusgdaud";
System.out.println(str.hashCode());
}
}
HashSet版本原理解析:数组+链表+(结合哈希算法)
- 创建一个默认长度16的数组,数组名table
Set<String> sets = new HashSet<>;
2.根据元素的哈希值跟元素的长度求余计算出应存入的位置(哈希算法)
3.判断当前位置是否为NULL,如果是Null直接存入
4.如果位置不为null,表示有元素,则调用equals方法比较
5.如果一样,则不存,如果不一样,则存入数组
- JDK7新元素占老元素位置,指向老元素
- JDK8中新元素挂在老元素下面
Jdk1.8版本开始HashSet原理解析
- 底层结构:哈希表(数组、链表、红黑树的结合体)
- 当挂在元素下面的数据过多时,查询性能降低,从JDK8开始后,当链表长度超过8的时候,自动转换成红黑树
哈希表的详细流程
- 创建一个默认长度16,默认加载因子为0.75的数组,数组名table
- 根据元素的哈希值跟数组的长度计算出应存入的位置
- 判断当前位置是否为null,如果是null直接存入,如果位置不为null,表示有元素,则调用equals方法比较属性值,如果一样,则不存,如果不一样则存入数组
- 当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的2倍
HashSet去重复原理解析
/*
定义学生类,并重写hashCode(),equals()方法
*/
import java.util.Objects;
public class Student{
private String name;
private char sex;
private int age;
public Student(String name, char sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex=" + sex +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return sex == student.sex && age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, sex, age);
}
}
/*
测试类
*/
import java.util.HashSet;
import java.util.Set;
public class SetDemo3 {
public static void main(String[] args) {
//理解hashSet的去重复原理解析
Set<Student> students = new HashSet<>();
students.add(new Student("张无忌", 'M', 20));
students.add(new Student("张三丰", 'M', 120));
students.add(new Student("乔峰", 'M', 30));
students.add(new Student("乔峰", 'M', 30));
System.out.println(students);
}
}
LinkedHashSet
LinkedhashSet集合概述和特点
- 有序、不重复、无索引
- 这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构依然时哈希表,只是每一个元素又额外的多了一个双链表的机制记录存储的顺序
TreeSet
- 不重复,无索引,可排序
- 可排序:按照元素的大小默认升序(从小到大排序)
- TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都比较好
- 注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序
TreeSet集合默认的规则
- 对于数值类型:Integer,Double,官方默认按照大小进行升序排序
- 对于字符串类型:默认按照首字符升序排序
- 对于自定义类型如:Student对象,TreeSet无法直接排序
注意:想要使用TreeSet存储自定义类型,需要制定排序规则
import java.util.Set;
import java.util.TreeSet;
public class SetDemo4 {
public static void main(String[] args) {
//观察TreeSet对有值特性对象的排序规则
Set<Integer> set1 = new TreeSet<>();
set1.add(12);
set1.add(454);
set1.add(2321);
set1.add(1);
System.out.println(set1);
Set<String> set2 = new TreeSet<>();
set2.add("shdhd");
set2.add("Afsdf");
set2.add("adsfs");
set2.add("ghdh");
System.out.println(set2);
}
}
import java.util.Objects;
public class Apple implements Comparable<Apple>{
private String name;
private int weight;
private double price;
public Apple() {
}
public Apple(String name, int weight, double price) {
this.name = name;
this.weight = weight;
this.price = price;
}
public String getName() {
return name;
}
public int getWeight() {
return weight;
}
public double getPrice() {
return price;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Apple apple = (Apple) o;
return weight == apple.weight && Double.compare(apple.price, price) == 0 && Objects.equals(name, apple.name);
}
@Override
public int hashCode() {
return Objects.hash(name, weight, price);
}
@Override
public String toString() {
return "Apple{" +
"name='" + name + '\'' +
", weight=" + weight +
", price=" + price +
'}';
}
@Override
public int compareTo(Apple o) {
return this.weight - o.weight;
}
}
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
public class SetDemo4 {
public static void main(String[] args) {
//观察TreeSet对有值特性对象的排序规则
Set<Integer> set1 = new TreeSet<>();
set1.add(12);
set1.add(454);
set1.add(2321);
set1.add(1);
System.out.println(set1);
Set<String> set2 = new TreeSet<>();
set2.add("shdhd");
set2.add("Afsdf");
set2.add("adsfs");
set2.add("ghdh");
System.out.println(set2);
System.out.println("====================");
// Set<Apple> apples = new TreeSet<>();
// apples.add(new Apple("红富士", 23, 23.5));
// apples.add(new Apple("红富士", 23, 24.5));
// apples.add(new Apple("红富士", 24, 23.5));
// apples.add(new Apple("红富士", 23, 23.5));
// System.out.println(apples); //按照比较规则进行去重
//方式2,使用集合自带比较器
Set<Apple> apples = new TreeSet<>(new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
//return o1.getWeight() - o2.getWeight();
return Double.compare(o1.getPrice(), o2.getPrice()); //按照价格升序排序
}
});
apples.add(new Apple("红富士", 23, 23.5));
apples.add(new Apple("红富士", 23, 24.5));
apples.add(new Apple("红富士", 24, 23.5));
apples.add(new Apple("红富士", 23, 26.5));
System.out.println(apples);
}
}
自定义排序规则
- TreeSet集合存储对象的时候有两种方式可以设计自定义比较规则
方式一
- 让自定义的类(如学生类)实现Comparable接口重写里面的compareTo方法来定制比较规则
方式二
- TreeSet集合有参构造器,可以设置comparator接口对应的比较器对象,来制定比较规则
两种方式中,关于返回值的规则
- 如果认为第一个元素大于第二个元素返回正整数即可
- 如果认为第一个元素小于第二个元素返回负整数即可
- 如果认为第一个元素等于第二个元素返回0即可,此时TreeSet集合只保留一个元素,认为两者重复