今日内容
上课同步视频:CuteN饕餮的个人空间_哔哩哔哩_bilibili
同步笔记沐沐霸的博客_CSDN博客-Java2301
零、 复习昨日
一、作业
二、Set
三、HashSet
四、LinkedHashSet
五、TreeSet
六、Collections
零、 复习昨日
提问过了…
一、作业
见代码…
二、Set
Set集合是Collection集合的子接口,该集合中不能有重复元素!!
Set集合提供的方法签名,与父接口Collection的方法完全一致!! 即没有关于下标操作的方法
Set接口,它有两个常用的子实现类HashSet,TreeSet
三、HashSet
HashSet实现了Set接口,底层是hash表(实际上底层是HashMap)
该类不允许重复
元素,不保证迭代顺序
,即无序(插入顺序和遍历顺序不一致)
3.1 方法演示
构造方法
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 );
}
3.2 扩容机制[面试]
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)
3.3 去重原理[面试]
- 调用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
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
TreeSet是基于
TreeMap
的NavigableSet
的实现.
可以使用元素的自然顺序对元素进行排序或者根据创建 TreeSet 时提供的Comparator
进行排序,具体取决于使用的构造方法即TreeSet会对存储的元素排序,当然也会去重!
5.1 方法演示
构造方法
- 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));
}
5.2 去重排序原理
前提知识: 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.3 练习
需求:创建 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去重,返回正负如何如何