一、Set
Set集合是Collection的子接口,代表一种集合,此种集合是元素不重复.
有两个常用实现类
HashSet 是元素不重复,无序,主要是指遍历顺序和插入顺序不一致
TreeSet 是元素不重复,排序
LinkedHashSet不常用
二、HashSet
1.1 介绍
HashSet是Set的实现类
底层是由哈希表实现,其实实际是HashMap实现
不允许重复,,无序,主要是指遍历顺序和插入顺序不一致
不保证线程安全,即不同步
方法与父接口Set,Collection中基本一致,就是最基本的集合方法,没有关于下标操作
1.2 演示API
练习常用方法
构造方法
添加,删除,清空,大小,迭代,toArray
掌握: 基本方法使用,演示出去重,遍历顺序问题!!!
public static void main(String[] args) {
HashSet<Integer> set = new HashSet<>();
set.add(11);
set.add(22);
set.add(33);
set.add(44);
set.add(55);
System.out.println(set.contains(11)); //判断是否包含某个元素
System.out.println(set.isEmpty()); //判断是否为空
set.remove(11); //移除某个元素
System.out.println(set.size()); //返回set的大小
Iterator<Integer> iterator = set.iterator(); //使用迭代器迭代set中的元素
while (iterator.hasNext()){
System.out.print(iterator.next()+" ");
}
for (Integer i : set) { //使用foreach遍历set中的元素,语法糖,等同与迭代器
System.out.print(i+" ");
}
}
1.3 底层原理
HashSet底层是哈希表
更直接的是HashSet底层是HashMap
创建HashSet时,其实创建了HashMap
向set集合add添加元素,其实放在HashMap集合中
HashSet去重的原理?
向set中存储运算时,调用元素本身的hashCode方法,获得地址值
如果这个地址值在set集合中如果没有相同,就将该元素存储到集合
如果这个地址值在set集合中有相同的,此时调用元素的equals来做判断
如果equals判断为true,属性全部相同,则不存储该元素,即去重
如果equals判断false,属性值有不相同,则存储该元素
简单说就是:
存储的元素地址值不一样,直接存储
地址值一样,再比较equals
eqauls判断不等,直接存储
equals判断相等,不存
三、LinkedHashSet
排序且去重
public static void main(String[] args) {
LinkedHashSet<Integer> lhs = new LinkedHashSet<>();
lhs.add(3);
lhs.add(3);
lhs.add(1);
lhs.add(5);
lhs.add(5);
lhs.add(4);
lhs.add(2);
System.out.println(lhs); //[3, 1, 5, 4, 2]
}
四、TreeSet
4.1 介绍
TreeSet是Set集合的实现类,也是元素不重复
底层是基于TreeMap实现的,TreeMap底层是基于红黑树(是一种特殊的二叉树)
会对存储的元素进行排序,默认按照自然顺序排序;或者是通过根据构造方法传入的比较器进行排序
4.2 方法api
仅介绍特殊方法
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>( );
// 去重,且默认是自然顺序排序
set.add(31);
set.add(13);
set.add(15);
set.add(41);
set.add(11);
set.add(21);
System.out.println(set );
Integer ceiling = set.ceiling(15); //返回set中大于等于给定的最小元素
System.out.println("ceiling = " + ceiling );
Integer higher = set.higher(15); //返回set中严格大于给定元素的最小元素
System.out.println("higher = " + higher);
}
4.3 排序+去重原理
通过向TreeSet<Studnet>中添加Student对象,发现会报错, 提示无法将Student转换成Comparable接口
-
从而发现,之前Integer,String等类能排序是因为它们实现Comparable接口,并且重写comparTo的方法
-
发现这个方法返回值决定了顺序和去重
-
当前(this)比指定小(o), 将其(this)放左树杈, 即返回负数,将数据放在左边
-
当前(this)比指定大(o), 将其(this)放右树杈,即返回正数,将数据放在右边
-
当前(this)等于指定(o), 0 不放,即返回0,不往树上放,则去除
-
package com.qf.set;
public class Student implements Comparable<Student>{
private int age;
private String name;
// set get 构造 toString 省略...
@Override
public int compareTo(Student o) {
int n;
if (this.age == o.getAge() && this.name.equals(o.getName())){
n = 0; //返回0则去重
} else if (this.age >= o.getAge()){
n = 1; //返回正数放节点右边
} else {
n = -1; //返回负数放节点左边
}
return n;
}
}
4.4 比较器排序
new TreeSet() 空参的,默认是使用原始的自然排序,
new TreeSet(Comparator c), 构造方法传入比较器,就会根据比较器排序
// 自定义比较器类,完成对整数倒序排序
public class MyIntegerComparator implements Comparator<Integer> {
/**
* o1 就是当前正在存储的
* o2 就是之前存储的
*/
@Override
public int compare(Integer o1, Integer o2) {
System.out.println("o1 = " + o1);
System.out.println("o2 = " + o2);
return o2 - o1;
}
}
// 测试
public class Demo9 {
public static void main(String[] args) {
// 演示1: 创建自定义比较器类对象
//TreeSet<Integer> set = new TreeSet<>(new MyIntegerComparator());
// 演示2: 改写成匿名内部类写法
// TreeSet<Integer> set =
// new TreeSet<>(new Comparator<Integer>( ) {
// @Override
// public int compare(Integer o1, Integer o2) {
// return o2 - o1;
// }
// });
new TreeSet<>((o1,o2) -> o2 - o1);
set.add(3);
set.add(2);
set.add(5);
set.add(1);
set.add(4);
System.out.println(set );
}
}