Set
Setjava.util.Set
接口和java.util.List
接口一样,同样继承自Collection
接口,
1.它与Collection
接口中的方法基本一致,并没有对Collection
接口进行功能上的扩充,只是比Collection
接口更加严格了。
2.与List
接口不同的是,Set
接口都会以某种规则保证存入的元素不出现重复。
HashSet集合介绍
java.util.HashSet
是Set
接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不能保证不一致)。java.util.HashSet
底层的实现其实是一个java.util.HashMap
支持,由于我们暂时还未学习,先做了解。
HashSet
是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存储和查找性能。
保证元素唯一性的方式依赖于:hashCode
与equals
方法。
public static void main(String[] args) { //创建 Set集合 HashSet<String> set = new HashSet<String>(); //添加元素 set.add(new String("cba")); set.add("abc"); set.add("bac"); set.add("cba"); //遍历 for (String name : set) { System.out.println(name); } } OUT: cba abc bac
4.2 HashSet集合存储数据的结构(哈希表)
什么是哈希表呢?
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用数组处理冲突,同一hash值的链表都存储在一个数组里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。
而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示。
看到这张图就有人要问了,这个是怎么存储的呢?
为了方便大家的理解我们结合一个存储流程图来说明一下:
总而言之,JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。
如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。
HashSet存储自定义类型元素
给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,
建立自己的比较方式,才能保证HashSet集合中的对象唯一
创建自定义Student类:
public class Student { private String name; private int age; //get/set @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 && Objects.equals(name, student.name); } @Override public int hashCode() { return Objects.hash(name, age); } }
创建测试类:
public class HashSetDemo2 { public static void main(String[] args) { //创建集合对象 该集合中存储 Student类型对象 HashSet<Student> stuSet = new HashSet<Student>(); //存储 Student stu = new Student("于谦", 43); stuSet.add(stu); stuSet.add(new Student("郭德纲", 44)); stuSet.add(new Student("于谦", 43)); stuSet.add(new Student("郭麒麟", 23)); stuSet.add(stu); for (Student stu2 : stuSet) { System.out.println(stu2); } } } 执行结果: Student [name=郭德纲, age=44] Student [name=于谦, age=43] Student [name=郭麒麟, age=23]
LinkedHashSet
我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢?
在HashSet下面有一个子类java.util.LinkedHashSet
,它是链表和哈希表组合的一个数据存储结构。
public class LinkedHashSetDemo { public static void main(String[] args) { Set<String> set = new LinkedHashSet<String>(); set.add("bbb"); set.add("aaa"); set.add("abc"); set.add("bbc"); Iterator<String> it = set.iterator(); while (it.hasNext()) { System.out.println(it.next()); } } } 结果: bbb aaa abc bbc
TreeSet集合
1. 特点
TreeSet集合是Set接口的一个实现类,底层依赖于TreeMap,是一种基于红黑树的实现,其特点为:
- 元素唯一
- 元素没有索引
- 使用元素的自然顺序对元素进行排序,或者根据创建 TreeSet 时提供的
Comparator
比较器
进行排序,具体取决于使用的构造方法:public TreeSet(): 根据其元素的自然排序进行排序 public TreeSet(Comparator<E> comparator): 根据指定的比较器进行排序
2. 演示
案例演示自然排序(20,18,23,22,17,24,19):
public static void main(String[] args) { //无参构造,默认使用元素的自然顺序进行排序 TreeSet<Integer> set = new TreeSet<Integer>(); set.add(20); set.add(18); set.add(23); set.add(22); set.add(17); set.add(24); set.add(19); System.out.println(set); } 控制台的输出结果为: [17, 18, 19, 20, 22, 23, 24]
案例演示比较器排序(20,18,23,22,17,24,19):
public static void main(String[] args) { //有参构造,传入比较器,使用比较器对元素进行排序 TreeSet<Integer> set = new TreeSet<Integer>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { //元素前 - 元素后 : 升序 //元素后 - 元素前 : 降序 return o2 - o1; } }); set.add(20); set.add(18); set.add(23); set.add(22); set.add(17); set.add(24); set.add(19); System.out.println(set); } 控制台的输出结果为: [24, 23, 22, 20, 19, 18, 17]