Set集合是Collection集合的子接口,该集合中不能有重复元素!!
Set集合提供的方法签名,与父接口Collection的方法完全一致!! 即没有关于下标操作的方法
Set接口,它有两个常用的子实现类HashSet,TreeSet
HashSet实现了Set接口,底层是hash表(实际上底层是HashMap)
该类不允许重复
元素,不保证迭代顺序
,即无序(插入顺序和遍历顺序不一致)
构造方法
HashSet()
构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16
,加载因子是 0.75
。HashSet(Collection extends E> c)
构造一个包含指定 collection 中的元素的新 set- HashSet(int initialCapacity) 构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。
- HashSet(int initialCapacity, float loadFactor) 构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。
方法
HashSet类中的方法与父接口Set接口中的方法一致,即又跟Collection接口中方法一致
…自行演示
public static void main(String[] args) {
// 创建HashSet集合
HashSet<Integer> set = new HashSet<>( );
// 放入元素
boolean r1 = set.add(221);
System.out.println(r1 );
boolean r2 = set.add(221);
System.out.println(r2 );
set.add(111);
set.add(111);
set.add(44);
set.add(23);
// 遍历(for + Iterator)
for(Integer i: set) {
System.out.println(i );
}
// 总结: 顺序问题+ 重复问题
// 无序即 插入顺序和迭代顺序不一致
// 不允许重复!
Iterator<Integer> iterator = set.iterator( );
while(iterator.hasNext()) {
Integer integer = iterator.next( );
System.out.println(integer );
}
// 演示其他方法(移除,判断,大小等等)
System.out.println(set.size() );
System.out.println(set.isEmpty( ));
set.clear();
System.out.println(set.size() );
System.out.println(set.isEmpty( ));
HashSet<Integer> set2 = new HashSet<>( );
set2.add(111);
// 移除全部指定元素
System.out.println(set.removeAll(set2));
System.out.println(set );
}
HashSet底层是Hash表,其实是HashMap.
默认初始容量16,加载因子0.75 —> 扩容的阈值= 容量 * 因子 = 16 * 0.75 = 12
即超过12个元素时就要触发扩容,扩容成原来的2倍(ps: 初始容量和加载因子是可以通过构造方法创建时修改的…)
练习1: 将字符串数组String [] arr = {“a”,“a”,“b”,“b”,“c”,“c”}去重,变成String[] arr = {“a”,“b”,c"};
// 思路: 遍历数组,将元素放入set集合,再将set集合转数组(toArray)
- 调用add(E e)方法时,会在底层调用元素e的hashcode方法来获得对象的地址值
- 如果地址值不一样,直接存储
- 如果地址值一样时,会再调用元素的equals方法判断元素的内容是否一样
- 如果equals为false,那么存储 但是如果equals判断值为true,那么去重
以后只需要使用工具生成hashcode和equals就可以再HashSet中去重!
public class Student{
// ..其他代码省略
// 重写hashcode和equalse
@Override
public int hashCode() {
return Objects.hash(age, name);
}
@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);
}
}
public static void main(String[] args) {
HashSet<Integer> set = new HashSet<>( );
set.add(1);
set.add(2);
set.add(3);
set.add(1);
System.out.println(set );
// 创建几个学生,放入set集合
// 设想属性一样的学生会去重
HashSet<Student> stuSet = new HashSet<>( );
stuSet.add(new Student(18,"zs"));
stuSet.add(new Student(19,"ls"));
stuSet.add(new Student(19,"ls"));
stuSet.add(new Student(20,"ww"));
System.out.println(stuSet );
}
LinkedHashSet 既有Set的去重的特性,又有Linked结构有序的特性,即
存储在LinkedHashSet 中的元素既不允许重复,又能保证迭代顺序
public static void main(String[] args) {
LinkedHashSet<Integer> lhs = new LinkedHashSet<>( );
lhs.add(211);
lhs.add(111);
lhs.add(111);
lhs.add(44);
lhs.add(44);
lhs.add(23);
System.out.println(lhs );
}
TreeSet是基于
TreeMap
的NavigableSet
的实现.
可以使用元素的自然顺序对元素进行排序或者根据创建 TreeSet 时提供的Comparator
进行排序,具体取决于使用的构造方法即TreeSet会对存储的元素排序,当然也会去重!
构造方法
- TreeSet()构造一个新的空 set,该 set 根据其元素的
自然顺序
进行排序。- TreeSet(Comparator super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。
方法
有常规的集合的方法(add,remove,Iterator,size等等),还有一些基于树结构能排序的特性才有的特殊方法,例如范围取值的(ceiling(),floor(),lower(),higher(),首尾取值(),first(),last()…)等操作
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>( );
set.add(44);
set.add(221);
set.add(221);
set.add(23);
set.add(111);
set.add(111);
set.add(0);
set.add(1114);
// 发现: 会排序(默认是升序),不能存储重复元素
System.out.println(set );
for(Integer i : set) {
System.out.println(i );
}
// 获得第一个(排序后)
System.out.println("first: " + set.first( ));
// 获得最后一个(排序后)
System.out.println("last: " + set.last() );
System.out.println(set );
// 获取并移除第一个(排序后)
System.out.println("第1个" + set.pollFirst());
// 获取并移除最后一个(排序后)
System.out.println("最hou1个" + set.pollLast());
System.out.println(set );
// 范围取值
System.out.println(set.lower(100));
}
前提知识: TreeSet底层是TreeMap,TreeMap是红黑树,是一种平衡二叉树(AVL)
练习1:新建User类(age,name),创建TreeSet集合,创建多个User对象,将user对象存入TreeSet集合,实现去重排序,1) 年龄和姓名一致则去重 2) 按照年龄从小到大排序
TreeSet<User> set = new TreeSet<>( );
set.add(new User(18,"厄加特"));
// 运行报错ClassCastException 无法转成Comparable接口
Comparable接口,强行对实现它的每个类的对象进行整体排序,这种排序被称为类的自然排序.
实现这个接口,需要重写comparTo方法,该方法返回值决定了是升序,降序还是去重!
该comparTo(T t)方法运行时 , this指代当前正在调用该方法的对象,参数T就是之前已经存在的元素.
- 返回值
0
,意味着此元素(正在存储的元素)和之前的元素相同
,即不存储,则去重
- 返回值
正整数
,意味着此元素大于
之前的元素, 放在该节点的右边
- 返回值
负整数
,意味着此元素小于
之前的元素,放在该节点的左边
最后都存储完毕时,取值时采用中序遍历(从根节点开始按照左,中,右的顺序读取)
public class User implements Comparable<User>{
// 属性和方法...
/**
* this 是指代正在存储的元素
* o 是之前存储的元素
*/
@Override
public int compareTo(User o) {
System.out.println("此对象--> " + this);
System.out.println("指定对象--> " + o);
// 姓名和年龄相同返回0,即去重不存储
if (this.name.equals(o.getName()) && this.getAge() - o.getAge() == 0) {
return 0;
}
// 年龄相同返回1,即保留下来的不去重的意思
// 年龄不同的话就正常相减,返回负数或正数
return this.getAge() - o.getAge() == 0 ? 1 : this.getAge() - o.getAge();
}
}
需求:创建 5个学生信息(姓名,语文成绩,数学成绩,英语成绩),放入TreeSet集合,输出时按照总分从高到低输出到控制台
package com.qf.set;
import java.util.TreeSet;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc
*/
public class StudentScore implements Comparable<StudentScore>{
private String name;
private int chinese;
private int math;
private int english;
// set get 省略
// 设计方法,获得总分
public int getTotal(){
return chinese+math+english;
}
@Override
public String toString() {
return "StudentScore{" +
"name='" + name + '\'' +
"总分='" + getTotal() + '\'' +
", chinese=" + chinese +
", math=" + math +
", english=" + english +
'}';
}
@Override
public int compareTo(StudentScore o) {
return o.getTotal() - this.getTotal() == 0 ? 1 : o.getTotal() - this.getTotal();
}
}
class TestStudentScore {
public static void main(String[] args) {
TreeSet<StudentScore> set = new TreeSet<>( );
set.add(new StudentScore("zhang3",70,70,70 ));
set.add(new StudentScore("wang5",100,100,100 ));
set.add(new StudentScore("li4",80,80,80 ));
set.add(new StudentScore("zhao6",60,60,60 ));
set.add(new StudentScore("zhou7",90,90,90 ));
for (StudentScore score : set) {
System.out.println(score );
}
}
}
HashSet 方法与父Collection接口中方法一致,正常记
需要向着面试准备: HashSet底层(HashMap),扩容,去重原理
LinkedHashSet 了解
TreeSet底层的树结构能了解就行,只需要指定要想去除排序,必须要实现接口重写方法,返回0去重,返回正负如何如何