Set
3.1Set集合概述和特点
public interface Set extends Collection
不包含重复元素的集合。
Set是一个接口,接口不能直接实例化,所以我们在创建Set对象的时候,使用Set接口的实现类HashSet来创建Set对象。
import java.util.HashSet;
import java.util.Set;
public class SetDemo01 {
public static void main(String[] args) {
//Set是一个接口,不能直接实例化,所以创建对象时创建实现类HashSet的对象
Set<String> set=new HashSet<String>();
set.add("hello");
set.add("world");
set.add("set");
set.add("collection");
set.add("world");//这个值添加不进去,因为set是不重复的
for(String s:set){
System.out.println(s);//输出是无序的
}
}
}
输出结果:
world
set
hello
collection
3.2 哈希值
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Obejct类中有一个方法可以获取对象的哈希值(hashCode())
1、同一个对象多次调用hashCode()获取的哈希值是一样的
2、默认情况下(使用Collection中的hashCode()),不同对象的哈希值是不一样的。即使不同对象的值一样,哈希值也不一样。
如果对hashCode()进行了重写,那么哈希值可能会相同。
3.3HashSet集合概述和特点
public class HashSet(E) extends AbstractSet
implements Set,Cloneable,Serailizable
该类实现Set集合,由哈希表实际为HashMap实例支持。对集合的迭代顺序不做任何保证。特别是它不能保证订单在一段时间内保持不变。这个类允许null元素。
HashSet集合特点:
1、底层数据结构是哈希表;
2、对集合的迭代顺序不做任何保证,也就是说不保证存储和取出的元素顺序一致;
3、没有带索引的方法,所以不能用普通for循环遍历;
4、由于是Set集合,所以是不包含重复元素的集合。
3.3.1HashSet集合存储字符串并遍历
这里需要注意的是:HashSet底层是Set集合,所以也是不允许重复元素的
import java.util.HashSet;
public class HashSetDemo01 {
public static void main(String[] args) {
HashSet<String> hashSet=new HashSet<String>();
hashSet.add("h");
hashSet.add("e");
hashSet.add("l");
hashSet.add("l");
hashSet.add("o");
for(String s:hashSet){
System.out.println(s);
}
}
}
输出结果:
e
h
l
o
3.4HashSet保证集合元素唯一性的源码分析
HashSet添加元素时,首先会去调用元素的**hashCode()计算元素的哈希值。hash值和元素的hashCode()**方法相关。
如果hash表未被初始化,就对其初始化;
再根据对象的hash值计算对象的存储位置,如果该位置没有元素,就存储元素;
存入元素和以前的元素比较哈希值
如果hash值不同,会继续向下执行,把元素添加到集合;
如果hash值相同,会调用对象的equal()方法比较;
调用equal方法返回true时,说明元素重复,不存储;
调用equal方法返回false时,会继续执行,添加元素。
HashCode集合存储元素:
需要保证元素唯一性,需要重写hashCode()和equals()
3.5常见数据结构之哈希表
哈希表:
JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组;
JDK8以后,在长度比较长的时候,底层实现了优化。
构造方法:
HashSet():构造一个新的空集合;背景HashMap()实例具有默认初始容量(16)和负载因子(0.75)
3.5.1案例:HashSet集合存储学生对象并遍历
需求:创建一个存储对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合。
要求:学生对象的成员变量值相同,我们就认为是同一个对象。
思路:
1、定义学生类
2、创建HashSet集合对象
3、创建学生对象
4、将学生对象添加到集合中
5、循环遍历集合
6、在学生类中重新两个方法
hashCode()和equals()->可以自动生成
如果不重写这两个方法,那么循环输出的结果会重复
package daily_collection;
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 boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
package daily_collection;
import java.util.HashSet;
public class HashSetDemo02 {
public static void main(String[] args) {
HashSet<Student> hs=new HashSet<Student>();
Student s1=new Student("ALL",1);
Student s2=new Student("IS",2);
Student s3=new Student("WELL",3);
Student s4=new Student("WELL",3);
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
for(Student s:hs){
System.out.println(s.getName()+"-"+s.getAge());
}
}
}
3.6LinkedHashSet集合概述和特点
public LinkedHashSet extends HashSet
implements Set,Cloneable,Serializable
LinkedHashSet是哈希表和链表实现Set接口,具有可预测的迭代次序。该实现与HashSet的不同之处在于它保持双向链表的所有条目。
LinkedHashSet集合特点:
1、哈希表和链表实现Set接口,具有可预测的迭代次序;
2、由链表保证元素有序,也就是说元素的存储和取出顺序是一致的;
3、由哈希表保证元素唯一,也就是说没有重复的元素。
3.6.1LinkedHashSet集合存储元素并遍历
import java.util.LinkedHashSet;
public class LinkedHashSetDemo01 {
public static void main(String[] args) {
LinkedHashSet<String> lhs=new LinkedHashSet<String>();
lhs.add("h");
lhs.add("e");
lhs.add("l");
lhs.add("l");
lhs.add("o");
for(String s:lhs){
System.out.println(s);
}
}
}
3.7TreeSet集合概述和特点
public class TreeSet extends AbstractSet
implements NavigableSet,Cloneable,Serializable.
TreeSet集合特点:
1、元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方法取决于构造方法
TreeSet():根据元素的自然排序进行排序;
TreeSet(Comparator comparator):根据指定的比较器进行排序。
2、没有带索引的方法,所以不能使用普通for循环遍历
3、由于是Set()集合,所以不包含重复元素的集合
3.7.1TreeSet集合存储整数并遍历
使用无参构造进行自然排序:
import java.util.TreeSet;
public class TreeSetDemo01 {
public static void main(String[] args) {
TreeSet<Integer> treeSet=new TreeSet<Integer>();
treeSet.add(2);
treeSet.add(13);
treeSet.add(7);
treeSet.add(5);
treeSet.add(5);//treeSet也是不重复的,此元素不会被添加
for (Integer i:treeSet){
System.out.println(i);
}
}
}
3.8自然排序Comparable的使用
需求:存储学生对象并遍历,创建TreeSet集合使用无参构造方法;
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序。
@Override
public int compareTo(Student o) {
// return 0;//返回0代表元素是重复的
// return 1;//返回1代表元素是不重复的,按照存储顺序输出
// return -1;//返回-1,按照降序存储
//按照年龄的降序排列
int num = this.age-o.age;
int num2 = num==0?this.name.compareTo(o.name):num;//年龄相等时,按姓名比较
return num;
}
结论:
1、用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的;
2、自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(To)方法;
3、重写方法时,一定要注意排序规则,必须按照要求的主要条件和次要条件来写。
3.9比较器排序Comparator的使用
存储学生对象并遍历,创建TreeSet集合使用带参构造方法;
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序。
package daily_comparator;
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;
}
}
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo01 {
public static void main(String[] args) {
TreeSet<Student> treeSet = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int num = o2.getAge() - o1.getAge();
int num2 = num == 0 ? o1.getName().compareTo(o2.getName()) : num;
return num2;
}
});
Student s1 = new Student("xishi", 28);
Student s2 = new Student("diaochan", 12);
Student s3 = new Student("wangzhaojun", 43);
Student s4 = new Student("yangyuhuan", 33);
treeSet.add(s1);
treeSet.add(s2);
treeSet.add(s3);
treeSet.add(s4);
for (Student s : treeSet) {
System.out.println(s.getName() + "," + s.getAge());
}
}
}
结论:
1、用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的;
2、比较器排序就是让构造方法接受Comparator的实现类对象,重写compareTo(T o1,T o2)方法
3、重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写。
3.9.1案例:成绩排序
需求:用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合
要求:按照总分从高到底出现
思路:
1、定义学生类
2、创建TreeSet集合对象,通过比较器排序进行排序
3、创建学生对象
4、将学生对象添加到集合中
5、遍历集合
note:
这里需要注意的是,根据主要条件判断总分是否大于小于,没有包含等于的情况;所以有次要条件,总分相等时我们按照语文成绩的高低排序;其次还有如果语文成绩也相等时,那么还需要比较姓名,因为不同的人可能总分和语文成绩都相等,如果不再附加比较姓名的次要条件时,那么各科成绩相等但姓名不一致时,这种对象的数据不会参与排序。
package daily_comparator;
public class Student {
private String name;
private int chinese;
private int math;
public Student() {
}
public Student(String name, int chinese, int math) {
this.name = name;
this.chinese = chinese;
this.math = math;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
public int getSum(){
return this.chinese+this.math;
}
}
package daily_comparator;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo02 {
public static void main(String[] args) {
TreeSet<Student> treeSet=new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int num=o2.getSum()-o1.getSum();
int num2=num==0?o1.getChinese()-o2.getChinese():num;
int num3=num2==0?o1.getName().compareTo(o2.getName()):num2;
return num3;
}
});
Student s1=new Student("Anna",87,99);
Student s2=new Student("Tom",95,90);
Student s3=new Student("Luna",84,88);
Student s4=new Student("John",93,92);
Student s5=new Student("John1",93,92);
treeSet.add(s1);
treeSet.add(s2);
treeSet.add(s3);
treeSet.add(s4);
treeSet.add(s5);
for(Student s:treeSet){
System.out.println(s.getName()+"-"+s.getSum());
}
}
}
3.9.2案例:不重复随机数
需求:编写一个程序,获取10个1-20之间的随机数,要求随机数不重复,并在控制台输出。
思路:
1、创建Set集合对象;
2、创建随机数对象;
3、判断集合的长度是都小于10;
是:产生一个随机数,添加到集合
回到3继续
4、遍历集合。
package daily_comparator;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
public class TreeSetDemo03 {
public static void main(String[] args) {
Set<Integer> set=new HashSet<Integer>(){};
Random r =new Random();
while(set.size()<10){
int n = r.nextInt(20) + 1;
set.add(n);
}
for(Integer i:set){
System.out.println(i);
}
}
}