Java学习之路(八):Set
Set集合概述以及特点:
- set 是一个不包含重复元素的collection
- set只是一个接口,一般使用它的子类HashSet,LinkedHashSet,TreeSet
HashSet
- 此类是Set接口的实现类,有哈希表支持
- 它不保证set的迭代顺序,特别是不保证顺序的恒久不变
- 此类允许使用null元素
我们一般使用HashSet来去重:
- 我们需要重写累的hashCode()和equals()方法
HashSet去重的依据:
首先会比较hashCode(),如果不一样则存进去,如果一样的话,调用equals()方法进行比较,如果返回True就不存储,false存储
面试题:为什么自动生成hashCode的时候有个31的数?
31是一个质数,质数是能被1和自己本身整除的数,没有公约数
31刚刚好,既不大也不小
31这个数好算,2的五次方-1,相当于向左移5位-1
案例:产生10个1-20之间的随机数要求随机数不能重复
package l1; import java.util.HashSet; import java.util.Random; import java.util.Set; public class Demo01 { public static void main(String[] args) { // TODO Auto-generated method stub Set<Integer> set = new HashSet<Integer>(); Random rd = new Random(); while(set.size()!=10){ int r = rd.nextInt(21); //生成1-20之间的随机数 if(r!=0){ set.add(r); } } System.out.println(set); } }
案例:从键盘读取一行输入,去掉重复字符并打印
package l2; import java.util.HashSet; import java.util.Scanner; import java.util.Set; public class Demo2 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入一行内容"); String line = sc.nextLine(); //将字符串转化为字符数组 char[] ch = line.toCharArray(); Set<Character> set = new HashSet<Character>(); for(char a:ch){ set.add(a); } System.out.println(set); } }
案例:使用HashSet()过滤List集合中的重复的元素
package l3; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class Demo3 { public static void main(String[] args) { List<String> li = new ArrayList<String>(); li.add("A"); li.add("B"); li.add("A"); li.add("A"); Set<String> set = new HashSet<String>(); set.addAll(li); System.out.println(set); } }
LinkedHashSet的概述和使用
- LinkedHashSet是一个具有可预知迭代顺序的Set接口
- 内部实现是使用哈希表和链接列表
- LinkedHashSet的特点是可以保证怎么存就怎么取
- LinkedHashSet是set集合中唯一一个能保证怎么存就怎么取的集合对象
- LinkedHashSet是HashSet的子类,所以也是保证元素唯一的,实现原理和HashSet一样
TreeSet的概述和使用
- TreeSet是一个可以用于排序的集合、
- TreeSet给予TreeMap的NavigableSet实现
- TreeSet的排序方法有两种:
- 使用元素的自然顺序Comparable对元素进行排序
- 使用构造方法的Comparator进行排序
使用TreeSet 存储自定义对象:
注意:使用TreeSet存储自定义对象是会出现异常:xxxx cannot be cast to java.lang.Comparable
- 如果想用TreeSet存储自定义对象,这个对象必须要实现Comparable接口
- 此接口强行堆实现它的每个类的对象进行排序
- 这种排序被称为自然排序,类的compareTo方法被成为它的自然比较方法
- 当compareTo方法返回0的时候,集合只会保留一个元素
- 当compareTo方法返回正数的时候,集合怎么存就怎么取
- 当compareTo方法返回负数的时候,集合会倒序存储
TreeSet存储自定义对象并遍历
package lesson0001; import java.util.Set; import java.util.TreeSet; public class Demo1 { public static void main(String[] args) { Set<Student> set = new TreeSet<Student>(); set.add(new Student(1,"n1")); set.add(new Student(2,"n2")); set.add(new Student(3,"n3")); for(Student std:set){ System.out.println(std); } } } class Student implements Comparable{ private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student [age=" + age + ", name=" + name + "]"; } public Student() { super(); // TODO Auto-generated constructor stub } public Student(int age, String name) { super(); this.age = age; this.name = name; } @Override public int compareTo(Object arg0) { return 1; } }
以上是最简单的一个例子,然后呢,我们现在需要实现:
TreeSet存储自定义对象并且遍历,而且要按照姓名长度,字母,年龄顺序排序
- 通过比较字符串的compareTo比较大小
- 排序按照unicode码的大小进行排序
- 防止名字相同,但年龄不同的Bug
package lesson0002; import java.util.Set; import java.util.TreeSet; public class Demo1 { public static void main(String[] args) { Set<Student> set = new TreeSet<Student>(); set.add(new Student(1,"aaaa")); set.add(new Student(5,"aaaa")); set.add(new Student(2,"aaaa")); set.add(new Student(1,"aaa")); set.add(new Student(1,"aa")); for(Student std:set){ System.out.println(std); } } }
package lesson0002; public class Student implements Comparable<Student> { private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Student(int age, String name) { super(); this.age = age; this.name = name; } public Student() { super(); // TODO Auto-generated constructor stub } @Override public int compareTo(Student o) { int length = this.name.length() - o.name.length(); int num = length == 0?this.name.compareTo(o.name):length; return num == 0?this.age-o.age:num; } @Override public String toString() { return "Student [age=" + age + ", name=" + name + "]"; } }
TreeSet除了这种自定义排序意外还可以定义一种自定义排序:比较器(Comparator)
package lesson0003; import java.util.Comparator; import java.util.Set; import java.util.TreeSet; //比较器排序 public class Demo1 { public static void main(String[] args) { Set<String> set = new TreeSet<String>(new Comparator<String>() { @Override public int compare(String o1, String o2) { int num = o1.compareTo(o2);//默认的从小到大 num = num==0?1:num; //如果相同返回1,即保存同名的元素 return num; } }); set.add("aaa"); set.add("bbb"); set.add("ccc"); set.add("aaa"); set.add("aa"); for(String s:set){ System.out.println(s); } } }
TreeSet的总结
TreeSet是用来排序的,可以指定一个顺序,对象存入之后会按照指定的顺序排序
TreeSet的排序方式有两种:
- 自然排序
- 比较器顺序
自然顺序:
- TreeSet类的add方法会把存入的对象提升为Comparable类型
- 调用对象的compareTo方法进行比较
- 根据compareTo方法返回的结果进行一个存储
比较器顺序:
- 创建TreeSet的时候可以定制一个Comparator
- 如果传入了Comparator的子类对象,那么TreeSet就会按照比较器的规则比较
- add方法内部会调用Comparator接口中的compare方法排序
- 调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数
自然排序和比较器排序的区别:
- TreeSet构造函数什么都不传,默认按照类中的Comparable的排序
- TreeSet如果传入Comparator,就优先按照Comparator(即,比较器排序要优先)
练习题案例:
1.在一个集合中存储了无序并且重复的字符串,让其有序,而且不能去除重复
我上面例子中,比较器排序的那个例子就是
2.从键盘输入多个整数,直到输入quit时结束输入。把所有输入的整数倒叙排列打印
package lesson0004; import java.util.Comparator; import java.util.Scanner; import java.util.Set; import java.util.TreeSet; public class Demo01 { public static void main(String[] args) { Set<Integer> set = new TreeSet<Integer>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { // TODO Auto-generated method stub return (o2-o1)==0?1:(o2-o1); // return o2-o1; } }); Scanner sc = new Scanner(System.in); while(true){ String str = sc.nextLine(); if("quit".equals(str)){ break; }else{ set.add(Integer.parseInt(str)); } } for(Integer i:set){ System.out.println(i); } } }
3.键盘录入学生信息按照总分排序后输出在控制台
package lesson0005; import java.util.Comparator; import java.util.Scanner; import java.util.Set; import java.util.TreeSet; public class Demo1 { public static void main(String[] args) { // TODO Auto-generated method stub Set<Student> set = new TreeSet<Student>(); Scanner sc = new Scanner(System.in); while(true){ System.out.println("请按照格式输入学生信息(学生名:年龄:成绩)"); String line = sc.nextLine(); if("quit".equals(line)){ break; }else{ String name = line.split(":")[0]; int age = Integer.parseInt(line.split(":")[1]); int score = Integer.parseInt(line.split(":")[2]); set.add(new Student(name,age,score)); } } for(Student std:set){ System.out.println(std); } } }
package lesson0005; public class Student implements Comparable<Student> { private String name; private int age; private int score; 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; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + ", score=" + score + "]"; } public Student() { super(); } public Student(String name, int age, int score) { super(); this.name = name; this.age = age; this.score = score; } @Override public int compareTo(Student o) { int num = o.score - this.score; num = num==0?this.name.compareTo(o.name):num; num = num==0?(this.age-o.age):num; return num==0?1:num; } }