Set系列集合的简述及使用
一.HashSet集合
特点:
添加的元素,是无序,不重复,无索引的。
代码演示:
public class HashSetDemo01 {
public static void main(String[] args) {
// 无序,不重复,无索引的。
Set<String> sets = new HashSet<>(); // 一行经典代码!!
sets.add("Mybatis");
sets.add("Java");
sets.add("Java");
sets.add("MySQL");
sets.add("MySQL");
sets.add("Spring");
// [Java, MySQL, Spring, Mybatis]
System.out.println(sets);
}
}
研究两个问题(面试热点):
1)Set集合添加的元素是不重复的,是如何去重复的?
1.对于有值特性的,Set集合可以直接判断进行去重复。
2.对于引用数据类型的类对象,Set集合是按照如下流程进行是否重复的判断。
Set集合会让两两对象,先调用自己的hashCode()方法得到彼此的哈希值(所谓的内存地址)
然后比较两个对象的哈希值是否相同,如果不相同则直接认为两个对象不重复。
如果哈希值相同,会继续让两个对象进行equals比较内容是否相同,如果相同认为真的重复了
如果不相同认为不重复。
代码演示
public class HashSetDemo02 {
public static void main(String[] args) {
Set<Integer> sets = new HashSet<>(); // 一行经典代码!!
sets.add(1);
sets.add(1);
sets.add(2);
sets.add(2);
System.out.println(sets);//[1, 2]
// 存储一些自定义类型数据:无序不重复
Set<Apple> apples = new HashSet<>();
Apple a1 = new Apple("红富士",59.9 ,"红色");
Apple a2 = new Apple("阿克苏",39.9 ,"青红色");
Apple a3 = new Apple("阿克苏",39.9 ,"青红色");
System.out.println(a1.hashCode()); // 哈希值,相当于是内存地址
System.out.println(a2.hashCode()); // 哈希值,相当于是内存地址
System.out.println(a3.hashCode()); // 哈希值,相当于是内存地址
apples.add(a1);
apples.add(a2);
apples.add(a3);
System.out.println(apples);
//[Apple{name = 阿克苏, price = 39.9, color = 青红色}, Apple{name = 红富士, price = 59.9, color = 红色}]
}
}
小结:
如果希望Set集合认为两个对象只要内容一样就重复了,必须重写对象的hashCode和equals方法。
2)Set集合元素无序的原因是什么?
Set系列集合添加元素无序的根本原因是因为底层采用了哈希表存储元素。
JDK 1.8之前:哈希表 = 数组 + 链表 + (哈希算法)
JDK 1.8之后:哈希表 = 数组 + 链表 + 红黑树 + (哈希算法)
当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
代码演示
public class HashSetDemo03 {
public static void main(String[] args) {
Set<String> sets = new HashSet<>(); // 一行经典代码!!
sets.add("Java");
sets.add("Java");
sets.add("Mybatis");
sets.add("MySQL");
sets.add("MySQL");
sets.add("Spring");
// [Java, MySQL, Spring, Mybatis]
System.out.println(sets);
}
}
二.LinkedHashSet集合
特点:
是HashSet的子类,元素是“有序” 不重复,无索引.
LinkedHashSet底层依然是使用哈希表存储元素的,
但是每个元素都额外带一个链来维护添加顺序!!
不光增删查快,还有序。缺点是多了一个存储顺序的链会占内存空间!!而且不允许重复,无索引。
代码演示
public class HashSetDemo04 {
public static void main(String[] args) {
// 有序不重复无索引
Set<String> sets = new LinkedHashSet<>();
sets.add("Mybatis");
sets.add("Java");
sets.add("Java");
sets.add("MySQL");
sets.add("MySQL");
sets.add("Spring");
// [Java, MySQL, Spring, Mybatis]
System.out.println(sets);
}
}
三.TreeSet集合
特点:
TreeSet: 不重复,无索引,按照大小默认升序排序!!
TreeSet集合称为排序不重复集合,可以对元素进行默认的升序排序。
TreeSet集合自自排序的方式:
1.有值特性的元素直接可以升序排序。(浮点型,整型)
2.字符串类型的元素会按照首字符的编号排序。
3.对于自定义的引用数据类型,TreeSet默认无法排序,执行的时候直接报错,因为人家不知道排序规则。
自定义的引用数据类型的排序实现:
对于自定义的引用数据类型,TreeSet默认无法排序
所以我们需要定制排序的大小规则,程序员定义大小规则的方案有2种:
a.直接为对象的类实现比较器规则接口Comparable,重写比较方法(拓展方式)
// 如果程序员认为比较者大于被比较者 返回正数!
// 如果程序员认为比较者小于被比较者 返回负数!
// 如果程序员认为比较者等于被比较者 返回0!
b.直接为集合设置比较器Comparator对象,重写比较方法
// 如果程序员认为比较者大于被比较者 返回正数!
// 如果程序员认为比较者小于被比较者 返回负数!
// 如果程序员认为比较者等于被比较者 返回0!
注意:如果类和集合都带有比较规则,优先使用集合自带的比较规则。
代码演示
public class TreeSetDemo05 {
public static void main(String[] args) {
// TreeSet : 排序不重复集合。
Set<Double> scores = new TreeSet<>();
scores.add(100.0);
scores.add(99.9);
scores.add(69.5);
scores.add(0.1);
scores.add(89.3);
System.out.println(scores);//[0.1, 69.5, 89.3, 99.9, 100.0]
// 字符串按照首字符的编号进行排序。
Set<String> names = new TreeSet<>();
names.add("Jack");
names.add("rose");
names.add("Dlei");
names.add("about");
names.add("曹雪芹");
names.add("bozai");
names.add("caocao");
names.add("angel");
System.out.println(names);//[Dlei, Jack, about, angel, bozai, caocao, rose, 曹雪芹]
// 引用数据类型定义TreeSet集合。
Set<Employee> employees = new TreeSet<>();
employees.add(new Employee("播仔",6500.0,21));
employees.add(new Employee("播妞",7500.0,19));
employees.add(new Employee("乔治",4500.0,23));
System.out.println(employees);// System.out.println(employees);//[Employee{name = 播妞, salary = 7500.0, age = 19}, Employee{name = 播仔, salary = 6500.0, age = 21}, Employee{name = 乔治, salary = 4500.0, age = 23}]
// public TreeSet(Comparator<? super E> comparator)
// 集合自带比较器对象
// 如果类和集合都存在大小规则,默认使用集合自带的规则进行大小排序!!
Set<Employee> employees1 = new TreeSet<>(new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
// o1比较者 o2被比较者
// 如果程序员认为比较者大于被比较者 返回正数!
// 如果程序员认为比较者小于被比较者 返回负数!
// 如果程序员认为比较者等于被比较者 返回0!
return o1.getAge() - o2.getAge();
}
});
employees1.add(new Employee("播仔",6500.0,21));
employees1.add(new Employee("播妞",7500.0,19));
employees1.add(new Employee("乔治",4500.0,23));
System.out.println(employees1);//[Employee{name = 播妞, salary = 7500.0, age = 19}, Employee{name = 播仔, salary = 6500.0, age = 21}, Employee{name = 乔治, salary = 4500.0, age = 23}]
}
}
**总结**
如果希望元素可以重复,又有索引,查询要快用ArrayList集合。(用的最多)
如果希望元素可以重复,又有索引,增删要快要用LinkedList集合。(适合查询元素比较少的情况,经常要首尾操作元素的情况)
如果希望增删改查都很快,但是元素不重复以及无序无索引,那么用HashSet集合。
如果希望增删改查都很快且有序,但是元素不重复以及无索引,那么用LinkedHashSet集合。
**错题**
set集合存储时无序,不重复,无无索引,
当是一个对象为泛型时,new的对象都是不重复的,即使内容重复,想要不重复就需要重写 hashCode 和equals方法