Day17JavaSE——Set集合&集合练习
文章目录
Set
HashSet
LinkedHashSet
TreeSet
集合练习
Set集合
Set集合的概述
//Set接口:不包含重复元素的集合
//list:允许元素重复
//Set接口的三个子类:HashSet、LinkedHashSet、TreeSet
//HashSet:底层数据结构是哈希表(实
// 际是HashMap实例),元素无序
//元素不能重复,可以存储null,线程不安全,效率高
//LinkedHashSet:底层数据结构是链表加哈希表,链表保证元素有序,哈希表表示元素唯一
//元素有序且唯一 线程不安全效率高
//TreeSet:底层数据结构是二叉树,元素唯一,且能对元素进行排序
HashSet
HashSet类概述
public class Test1 {
public static void main(String[] args) {
//HashSet构造方法
/***************************************************
* HashSet()
* 构造一个新的空集合; 背景HashMap实例具有默认初始容量(16)和负载因子(0.75)。
* HashSet(Collection<? extends E> c)
* 构造一个包含指定集合中的元素的新集合。
* HashSet(int initialCapacity)
* 构造一个新的空集合; 背景HashMap实例具有指定的初始容量和默认负载因子(0.75)。
* HashSet(int initialCapacity, float loadFactor)
* 构造一个新的空集合; 背景HashMap实例具有指定的初始容量和指定的负载因子。
* **************************************************/
HashSet<String> strings = new HashSet<>();
strings.add("aaa");
strings.add("bbb");
strings.add("ccc");
strings.add("ddd");
strings.add("eee");
strings.add("fff");
strings.add("ggg");
strings.add("kkk");
System.out.println(strings);
System.out.println("===================");
//遍历集合
for (java.lang.String string : strings) {
System.out.println(string);
}
Iterator<String> iterator1 = strings.iterator();
while (iterator1.hasNext()) {
System.out.print(iterator1.next().hashCode()+" ");
}
System.out.println();
System.out.println("===================");
HashSet<Integer> integers = new HashSet<>();
integers.add(100);
integers.add(200);
integers.add(300);
integers.add(400);
integers.add(500);
integers.add(600);
integers.add(700);
integers.add(800);
integers.add(900);
System.out.println(integers);
//遍历
for (Integer integer : integers) {
System.out.println(integer);
}
Iterator<Integer> iterator = integers.iterator();
while(iterator.hasNext()){
System.out.print(iterator.next().hashCode()+" ");
}
//由以上代码的运行结果可以看出,HashSet在存储数据时是无序的
//并且Integer和String类的原码看出都重写了hashCode函数和equals函数
}
}
自定义对象存储进HashSet & HashSet的
public class Test1 {
public static void main(String[] args) {
//HashSet底层数据结构是哈希表
//哈希表:是一个元素为链表的数组,综合了数组和链表的优点(JDK1 .7 之前)
//JDK1.8之后优化了(数组+链表+二叉树)
//每一个哈希表的值都是根据hashCode函数计算出来的
//当计算的哈希值一样,同一个数组单元中就会以单链表的形式存储第二个数
//理论上链表存储node的个数是没有上限的但是由于链表的不安全性从而人为的限制了链表存储node的个数
//哈希表存储图如图所示
HashSet<Student> students = new HashSet<>();
students.add(new Student("张三",23));
students.add(new Student("张三", 23));
students.add(new Student("李四", 24));
students.add(new Student("王五", 25));
students.add(new Student("赵六", 26));
students.add(new Student("赵六", 26));
students.add(new Student("田七", 27));
System.out.println("==========利用构造函数输出==========");
//利用Student中的get函数来遍历显示集合中的元素
for (Student student : students) {
System.out.println(student.getName()+"===="+student.getAge());
}
System.out.println("=========重写toString方法=========");
//重写toString方法,再遍历输出
for (Student student : students) {
System.out.println(student);
}
//当我们往hashset集合存储对象的时候,会调用对象的hashCode方法,算出一个值
//这个值就是确定这个对象放到表中的位置。
//那假如有两个对象,算出的位置值是一样的,就会调用equals()方法
//去比较两个对象的地址值是否一样,如果不一样那就存储进去,形成链表结构
//也就是说,当计算出的哈希值一样,就会发生碰撞,而碰撞后就会重复的去调用代码比较
//我们知道计算机执行代码是需要时间的,所以重复调用同一个方法就会使代码的运行效率降低
//当数据库数据很小时,碰撞引发的影响很小,可是当数据库庞大到一个数量级时
// 若是所有的哈希值都一样,就会极大的降低代码的效率,所以要重写hashCode函数来计算不同的哈希值来减少碰撞
//为了减少碰撞,我们应该合理的去重写hashCode方法减少碰撞(减少调用euquals方法的次数)
//尽量是元素在哈希表中横向排列,减少链表的形成
}
}
public class Student {
private String name;
private int age;
public Student(){}
public Student(String name,int age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/*HashSet:集合能够保证元素的唯一性,是靠元素重写hashCode()方法和equals()方法来保证的,
如果元素不重写hashCode()和equals方法,则无法保证的元素的唯一性
我们合理的重写 hashCode()方法 是为了元素能够在哈希表中尽量横向分布
减少碰撞(减少调用equals()方法的次数)*/
@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() {
//IDEA重写时,调用一个工具 Objects.hash(name, age); 根据成员变量,算出一个整数值
return Objects.hash(name, age);
//return this.hashCode()+this.age*9;
}
}
集合遍历
public class Test1 {
public static void main(String[] args) {
HashSet<Integer> integers = new HashSet<>();
integers.add(100);
integers.add(200);
integers.add(300);
integers.add(400);
integers.add(500);
integers.add(600);
integers.add(700);
integers.add(800);
integers.add(900);
System.out.println("=========使用迭代器遍历集合=========");
Iterator<Integer> iterator = integers.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("===========使用新for遍历===========");
for (Integer integer : integers) {
System.out.println(integer);
}
System.out.println("========使用forEache遍历集合========");
integers.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
}
}
集合去重
public class Test1 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(100);
list.add(100);
list.add(100);
list.add(200);
list.add(200);
list.add(300);
list.add(300);
//利用HashSet的构造函数去重
/* HashSet(Collection < ? extends E > c)
构造一个包含指定 collection 中的元素的新 set。*/
//将List集合传入这个构造函数就去重了
HashSet<Integer> integers = new HashSet<>(list);
System.out.println(integers);
}
}
LinkedHashSet
public class Test {
public static void main(String[] args) {
//LionkedHashSet 底层数据结构是链表加哈希表,链表保证元素有序,哈希表保证元素唯一
//LionkedHashSet 元素有序且唯一 线程不安全,效率高
//HashSet 元素无序且唯一
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("张三");
linkedHashSet.add("李四");
linkedHashSet.add("王五");
linkedHashSet.add("赵六");
linkedHashSet.add("田七");
linkedHashSet.add("刘八");
linkedHashSet.add("张三");
linkedHashSet.add("李四");
linkedHashSet.add("王五");
linkedHashSet.add("赵六");
linkedHashSet.add("田七");
linkedHashSet.add("刘八");
for (String s : linkedHashSet) {
System.out.println(s);
}
}
}
TreeSet
概述
//TreeSet:底层数据结构是二叉树,元素唯一,且能对元素进行排序
/*
* 存储下列元素:
* 20, 18, 23, 22, 17, 24, 19, 18, 24
* */
//自然排序:要求元素实现Comparable这个接口重写compareTo()方法
//根据比较的方法返回值的正负0 来决定元素在二叉树中放置的位置
public class Test {
public static void main(String[] args) {
TreeSet<Integer> integers = new TreeSet<>();
integers.add(20);
integers.add(18);
integers.add(23);
integers.add(22);
integers.add(17);
integers.add(24);
integers.add(19);
integers.add(18);
integers.add(24);
for (Integer integer : integers) {
System.out.println(integer);
}
//Integer类实现了Comparable这个接口,重写了compareTo
/*public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}*/
}
}
TreeSet的排序以及比较器排序的代码实现
//当调用TreeSet的空参构造的时候,就默认使用的是自然排序
//但是TreeSet还可以实现另一种排序——比较器排序
/*
* 方式一:自然排序,当你使用空参构造时,用的就是自然排序
* 如果用自然排序:自然排序对元素有要求,要求元素必须实现一个 Comparable 接口
* 重写这个接口中的compareTo()这个比较的方法
* 根据这个比较的方法的返回值的正负0 来决定元素的在二叉树中的放到位置,以及往不往里面放
* */
public class Test1 {
public static void main(String[] args) {
System.out.println("=========自然排序=========");
//使用自然排序:来排序学生对象,根据学生的年龄大小 来排序
/*画图解释自然排序调用的流程*/
TreeSet<Student> students = new TreeSet<>();
students.add(new Student("张三",33));
students.add(new Student("李四", 24));
students.add(new Student("王五", 38));
students.add(new Student("赵六", 26));
students.add(new Student("张三", 33));
students.add(new Student("张三", 33));
students.add(new Student("王老五", 33));
for (Student student : students) {
System.out.println(student);
}
System.out.println("=========================");
//使用自然排序:来排序学生对象,根据学生的姓名长度来排序,姓名长度短的放前面,姓名长度长的放后面
TreeSet<Student1> students1 = new TreeSet<>();
students1.add(new Student1("张三四",33));
students1.add(new Student1("李四", 24));
students1.add(new Student1("王五六七", 38));
students1.add(new Student1("赵六", 26));
students1.add(new Student1("张三", 33));
students1.add(new Student1("张三", 33));
students1.add(new Student1("王老五", 33));
for (Student1 student1 : students1) {
System.out.println(student1);
}
System.out.println("=========比较器排序=========");
/* TreeSet(Comparator < ? super E > comparator)
构造一个新的空 TreeSet,它根据指定比较器进行排序。*/
// Comparator 接口 强行对某个对象 collection 进行整体排序 的比较函数。
//采用比较器排序,根据比较器中的compare()这个方法的返回值的正负0 来决定元素在二叉树中放置的位置
TreeSet<Integer> treeSet= new TreeSet<>(new MyComparator());
treeSet.add(20);
treeSet.add(20);
treeSet.add(20);
treeSet.add(10);
treeSet.add(200);
treeSet.add(30000);
treeSet.add(120);
treeSet.add(200);
treeSet.add(20999);
for (Integer integer : treeSet) {
System.out.print(integer+" ");
}
}
}
//新建一个实现了比较器接口的自定义比较类,重写比较器
class MyComparator implements Comparator<Integer> {
@Override
public int compare(Integer a, Integer b) {
System.out.println("a:"+a+"==="+"b:"+b);
return a-b; //返回 正 负 0
}
}
注意:使用TreeSet集合进行元素的自然排序,那么对元素有要求,要求这个元素
必须实现Comparable接口 否则无法进行自然排序
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student(){}
public Student(String name, int age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/*HashSet:集合能够保证元素的唯一性,是靠元素重写hashCode()方法和equals()方法来保证的,
如果元素不重写hashCode()和equals方法,则无法保证的元素的唯一性
我们合理的重写 hashCode()方法 是为了元素能够在哈希表中尽量横向分布
减少碰撞(减少调用equals()方法的次数)*/
@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() {
//IDEA重写时,调用一个工具 Objects.hash(name, age); 根据成员变量,算出一个整数值
return Objects.hash(name, age);
//return this.hashCode()+this.age*9;
}
@Override
public int compareTo(Student o) {
//System.out.println("重写的compareTo函数被调用了");
//按照年龄的大小来比较
int num=this.age-o.age;
//年龄相同还得比较是否姓名也相同
int num2=num==0?this.name.compareTo(o.name):num;
//从小到大
//返回值的 正 负 0 来决定元素在二叉树中的放置的左右位置,返回0 就不往里面存储
return num2;
//从大到小
//return -num2;
}
}
public class Student1 implements Comparable<Student1>{
private String name;
private int age;
public Student1(){}
public Student1(String name, int age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/*HashSet:集合能够保证元素的唯一性,是靠元素重写hashCode()方法和equals()方法来保证的,
如果元素不重写hashCode()和equals方法,则无法保证的元素的唯一性
我们合理的重写 hashCode()方法 是为了元素能够在哈希表中尽量横向分布
减少碰撞(减少调用equals()方法的次数)*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student1 student = (Student1) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
//IDEA重写时,调用一个工具 Objects.hash(name, age); 根据成员变量,算出一个整数值
return Objects.hash(name, age);
//return this.hashCode()+this.age*9;
}
@Override
public int compareTo(Student1 o) {
//System.out.println("重写的compareTo函数被调用了");
//按照名字的长度来比较
int num=this.name.length()-o.name.length();
//名字长度相同还得比较是否姓名内容也相同
int num2=num==0?this.name.compareTo(o.name):num;
//姓名长度一样,姓名字面内容一样,还得比较年龄
int num3 = num2 == 0 ? this.age - o.age : num2;
//从小到大
//返回值的 正 负 0 来决定元素在二叉树中的放置的左右位置,返回0 就不往里面存储
return num3;
//从大到小
//return -num3;
}
}
TreeSet保证元素唯一和自然排序的原理和图解
TreeSet保证元素唯一和自然排序的原理和图解
二叉树的数据结构 先存入一个树根 分两个叉
存储元素时 跟树根比较 小的放在左边 大的放在右边
如果相等就不存储
取的时候按照 左中右的顺序来取
TreeSet存储自定义对象并遍历练习
public class Student {
private String name;
private int age;
public Student(){}
public Student(String name, int age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/*HashSet:集合能够保证元素的唯一性,是靠元素重写hashCode()方法和equals()方法来保证的,
如果元素不重写hashCode()和equals方法,则无法保证的元素的唯一性
我们合理的重写 hashCode()方法 是为了元素能够在哈希表中尽量横向分布
减少碰撞(减少调用equals()方法的次数)*/
@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() {
//IDEA重写时,调用一个工具 Objects.hash(name, age); 根据成员变量,算出一个整数值
return Objects.hash(name, age);
//return this.hashCode()+this.age*9;
}
}
public class Test1 {
public static void main(String[] args) {
//使用比较器排序,按照年龄大小排序
TreeSet<Student> students = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int i = o1.getAge() - o2.getAge();
//年龄一样,就需要比较姓名是否一样
int j = i == 0 ? o1.getName().compareTo(o2.getName()) : i;
return j;
}
});
students.add(new Student("乔布斯", 50));
students.add(new Student("尼古拉斯赵四", 57));
students.add(new Student("刘能", 57));
students.add(new Student("谢大脚", 57));
students.add(new Student("东北狠人彪哥药匣子", 23));
students.add(new Student("剪刀手爱德华", 97));
students.add(new Student("王祖贤", 47));
students.add(new Student("王菲", 17));
students.add(new Student("王菲", 17));
students.add(new Student("王菲", 87));
for (Student student : students) {
System.out.println(student);
}
System.out.println("====================================");
//使用比较器排序,按照姓名长度排序
TreeSet<Student> students1 = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int num = o1.getName().length() - o2.getName().length();
//判断姓名内容
int num2 = num == 0 ? o1.getName().compareTo(o2.getName()) : num;
//判断年龄
int num3 = num2 == 0 ? o1.getAge() - o2.getAge() : num2;
return num3;
}
});
students1.add(new Student("乔布斯", 50));
students1.add(new Student("尼古拉斯赵四", 57));
students1.add(new Student("刘能", 57));
students1.add(new Student("谢大脚", 57));
students1.add(new Student("东北狠人彪哥药匣子", 23));
students1.add(new Student("剪刀手爱德华", 97));
students1.add(new Student("王祖贤", 47));
students1.add(new Student("王菲", 17));
students1.add(new Student("王菲", 17));
students1.add(new Student("王菲", 87));
for (Student student : students1) {
System.out.println(student);
}
}
}
Comparetor比较器的使用
public class Test1 {
public static void main(String[] args) {
//Comparator这个比较器不光TreeSet能用,其他的类也能用
//将Comparator传递给sort函数(如 Collections.sort 或 Arrays.sort)
ArrayList<Integer> integers = new ArrayList<>();
integers.add(20);
integers.add(220);
integers.add(290);
integers.add(2);
integers.add(120);
integers.add(201);
integers.add(209);
//传入比较器,对List集合中的元素进行排序
integers.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
});
System.out.println(integers);
System.out.println("=========================");
int[] arr={1,55,2,44,9};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
System.out.println("=========================");
Integer[] arr2 = {20, 30, 90, 41, 33};
//参数2:可以传入比较器
Arrays.sort(arr2, new Comparator<Integer>() {
@Override
public int compare(Integer a, Integer b) {
return b-a; //降序排列
}
});
System.out.println(Arrays.toString(arr2));
}
}
集合练习
练习1
//需求:编写一个程序,获取10个1至20的随机数,要求随机数不能重复。
public class Test {
public static void main(String[] args) {
Random random = new Random();
LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>();
while (linkedHashSet.size() < 10) {
int i = random.nextInt(20) + 1;
linkedHashSet.add(i);
}
System.out.println(linkedHashSet);
}
}
练习2
/* A:
案例演示:
需求:键盘录入3个学生信息(姓名, 语文成绩, 数学成绩, 英语成绩), 按照总分从高到低输出到控制台*/
public class Test {
public static void main(String[] args) {
TreeSet<Student> students = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//按照学生的总分从高到低排序
int num = o1.getTotalScore() - o2.getTotalScore();
//总分一样,比较姓名
int num2 = num == 0 ? o1.getName().compareTo(o2.getName()):num;
return -num2;//降序排列
}
});
//录入学生信息,成绩
Scanner scanner = new Scanner(System.in);
System.out.println("请输入学生人数");
int studentNum = scanner.nextInt();
for (int i = 0; i < studentNum; i++) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入第"+i+"个学生的姓名");
String name = sc.nextLine();
System.out.println("请输入第" + i + "个学生的语文成绩");
int yw = sc.nextInt();
System.out.println("请输入第" + i + "个学生的数学成绩");
int sx= sc.nextInt();
System.out.println("请输入第" + i + "个学生的英语成绩");
int yy = sc.nextInt();
//创建学生对象,把录入的信息,封装到学生学生对象里面
Student student = new Student(name, yw, sx, yy);
//这三个对象,得找容器存起来
students.add(student);
}
//按照总分高低排序,输出学生的信息。
System.out.println("名次\t姓名\t语文\t数学\t英语\t总分");
int i=0;
for (Student student : students) {
System.out.println(student);
}
}
}
ner sc = new Scanner(System.in);
System.out.println("请输入第"+i+"个学生的姓名");
String name = sc.nextLine();
System.out.println("请输入第" + i + "个学生的语文成绩");
int yw = sc.nextInt();
System.out.println("请输入第" + i + "个学生的数学成绩");
int sx= sc.nextInt();
System.out.println("请输入第" + i + "个学生的英语成绩");
int yy = sc.nextInt();
//创建学生对象,把录入的信息,封装到学生学生对象里面
Student student = new Student(name, yw, sx, yy);
//这三个对象,得找容器存起来
students.add(student);
}
//按照总分高低排序,输出学生的信息。
System.out.println("名次\t姓名\t语文\t数学\t英语\t总分");
int i=0;
for (Student student : students) {
System.out.println(student);
}
}
}