Set
List是有序集合的根接口,Set是无序集合的根接口,无序也就意味着元素不重复。更严格地说,Set集合不包含一对元素e1和e2 ,使得e1.equals(e2) ,并且最多一个空元素。
使用Set存储的特点与List相反:元素无序、不可重复。常用的实现方式:HashSet、LinkedHashSet和TreeSet。
Set系列集合概述和特点
底层数据结构是哈希表
存取无序
不可以存储重复元素
没有索引,不能使用普通for循环遍历
set集合的基本应用
public static void main(String[] args) {
Set<String> s=new HashSet<String>();
boolean flag1= s.add("aaa");
boolean flag2 = s.add("aaa");
System.out.println(flag1);
System.out.println(flag2);
System.out.println(s);
//如果当前元素是第一次添加,那么可以添加成功,返回true
//如果当前元素是第二次添加,那么添加失败,返回false
}
存储字符串并遍历
方式一迭代器方式
public static void main(String[] args) {
Set<String> s=new HashSet<String>();
s.add("张三");
s.add("李四");
//迭代器
Iterator<String> it = s.iterator();
while(it.hasNext()){
String str = it.next();
System.out.println(str);
}
}
方式2-增强for
//增强for
for (String str : s) {
System.out.println(str);
}
方式3lambda表达式
// Lambda表达式
s.forEach((String str)-> System.out.println(str));
总结
HashSet集合概述和特点
底层数据结构是哈希表
存取无序
不可以存储重复元素
没有索引,不能使用普通for循环遍历
哈希值
哈希值简介
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
如何获取哈希值
Object类中的public int hashCode():返回对象的哈希码值
哈希值的特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的
默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
package set;
/*
哈希值:
对象的整数表现形式
1. 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
2. 如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
3. 但是在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)
*/
public class demo3 {
public static void main(String[] args) {
// 1. 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
Student s1 = new Student("zhangsan",18);
Student s2 = new Student("zhangsan",18);
/* System.out.println(s1.hashCode());//189568618
System.out.println(s2.hashCode());//793589513*/
//不一样,所以我们要重写hashCode()
// 2. 如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样
System.out.println("------------------");
System.out.println(s1.hashCode());//1461067297
System.out.println(s2.hashCode());//1461067297
// 3. 但是在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)
//哈希碰撞
System.out.println("abc".hashCode());//96354
System.out.println("acD".hashCode());//96354
}
}
练习
package lx;
import java.util.HashSet;
public class demo1 {
public static void main(String[] args) {
Student s1 = new Student("张三", 8);
Student s2 = new Student("张三", 8);
Student s3 = new Student("李四", 18);
Student s4 = new Student("王五", 17);
HashSet<Student> set = new HashSet<Student>();
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
//重写hashCode方法就可以去重复的对象
//因为重写hashCode方法,比的是属性值,属性值一样,哈希值一样,所以添加不成功
//不重写比的是地址值,创建出来的对象地址值永远不一样,所以哈希值不一样,所以添加成功
//如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样
//重写equals方法也一样 比的也是地址值,不是属性值
for (Student student : set) {
System.out.println(student);
}
}
}
注意像String Integer类型的,Java已经在底层重写好了HashSet和equals方法
LinkedHashSet
package LinkedHashSet;
import lx.Student;
import java.util.HashSet;
import java.util.LinkedHashSet;
public class demo1 {
public static void main(String[] args) {
Student s1 = new Student("张三", 8);
Student s2 = new Student("张三", 8);
Student s3 = new Student("李四", 18);
Student s4 = new Student("王五", 17);
LinkedHashSet<Student> set = new LinkedHashSet<Student>();
set.add(s3);
set.add(s1);
set.add(s3);
set.add(s4);
//LinkedHashSet的存和去顺序一样
for (Student student : set) {
System.out.println(student);
}
}
}
TreeSet
package TreeSet;
import java.util.TreeSet;
public class demo1 {
public static void main(String[] args) {
//利用TreeSet对整数进行排序
//默认升序
TreeSet<Integer> ts = new TreeSet<>();
//添加元素
ts.add(4);
ts.add(2);
ts.add(5);
ts.add(8);
ts.add(1);
//使用增强for
for (Integer t : ts) {
System.out.print(t + " ");
}
}
}
package TreeSet;
import java.util.TreeSet;
public class demo2 {
public static void main(String[] args) {
//利用TreeSet对String类型进行排序
TreeSet<String> ts = new TreeSet<>();
ts.add("va");
ts.add("aaa");
ts.add("ha");
ts.add("aba");
ts.add("acd");
for (String t : ts) {
System.out.print(t + " ");
}
}
}
练习
@Override
public int compareTo(Student o) {
//只看年龄按照升序排序
int tmp=this.getAge() - o.getAge();
tmp= tmp==0?this.getName().compareTo(o.getName()):tmp;
return tmp;
}
package TreeSet;
import javax.print.DocFlavor;
import java.util.TreeSet;
import java.util.function.Consumer;
public class demo3 {
public static void main(String[] args) {
//利用TreeSet对学生类型进行排序
TreeSet<Student> ts = new TreeSet<>();
//要求: 按照学生的年龄进行排序
//同年按照姓名字母排序
//同姓名,同年龄认为同一个人
Student s1 = new Student("zhangsan", 18);
Student s2 = new Student("lisi", 19);
Student s3 = new Student("wangwu", 19);
ts.add(s2);
ts.add(s3);
ts.add(s1);
System.out.println(ts);
//方式一:默认的排序方式,Student实现一个接口(comparable接口)重写里面的抽象方法
//再指定比较规则
//hashCode和equals方法跟哈希表有关
//TreeSet底层红黑树有关
//所以不需要重写hashCode和equals方法
}
}
package TreeSet;
import java.util.Comparator;
import java.util.TreeSet;
public class demo4 {
public static void main(String[] args) {
//方法二:比较器排序
//1.创建集合
//2.o1表示当前要添加的元素
//3.o2表示已经在红黑树存在的元素
//返回值规则跟之前一样
TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
@Override
/*
需求:请自行选择比较器排序和自然排序两种方式;
要求:存入四个字符串, “c”, “ab”, “df”, “qwer”
按照长度排序,如果一样长则按照首字母排序
采取第二种排序方式:比较器排序
*/
public int compare(String o1, String o2) {
//按照长度来
int tmp = o1.length() - o2.length();
//如果一样长则按照首字母排序
tmp = tmp == 0 ? o1.compareTo(o2) : tmp;
return tmp;
}
});
//2.添加元素
ts.add("c");
ts.add("ab");
ts.add("df");
ts.add("qwer");
System.out.println(ts);
}
}
package lx2;
import java.util.TreeSet;
/* 需求:创建5个学生对象
属性:(姓名,年龄,语文成绩,数学成绩,英语成绩),
按照总分从高到低输出到控制台
如果总分一样,按照语文成绩排
如果语文一样,按照数学成绩排
如果数学成绩一样,按照英语成绩排
如果英文成绩一样,按照年龄排
如果年龄一样,按照姓名的字母顺序排
如果都一样,认为是同一个学生,不存。
第一种:默认排序/自然排序
第二种:比较器排序
默认情况下,用第一种排序方式,如果第一种不能满足当前的需求,采取第二种方式。
课堂练习:
要求:在遍历集合的时候,我想看到总分。
*/
public class demo1 {
public static void main(String[] args) {
//1.创建学生对象
Student s1 = new Student("zhangsan", 23, 90, 99, 50);
Student s2 = new Student("lisi", 24, 90, 98, 50);
Student s3 = new Student("wangwu", 25, 95, 100, 30);
Student s4 = new Student("zhaoliu", 26, 60, 99, 70);
Student s5 = new Student("qianqi", 26, 70, 80, 70);
TreeSet<Student> st=new TreeSet<>();
st.add(s1);
st.add(s2);
st.add(s3);
st.add(s4);
st.add(s5);
for (Student student : st) {
System.out.println(student);
}
}
}
package lx2;
//第一种:默认排序/自然排序
public class Student implements Comparable<Student> {
private String name;
private int age;
//语文成绩
private int chinese;
//数学成绩
private int math;
//英语成绩
private int english;
public Student() {
}
public Student(String name, int age, int chinese, int math, int english) {
this.name = name;
this.age = age;
this.chinese = chinese;
this.math = math;
this.english = english;
}
/**
* 获取
*
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
*
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
*
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
*
* @return chinese
*/
public int getChinese() {
return chinese;
}
/**
* 设置
*
* @param chinese
*/
public void setChinese(int chinese) {
this.chinese = chinese;
}
/**
* 获取
*
* @return math
*/
public int getMath() {
return math;
}
/**
* 设置
*
* @param math
*/
public void setMath(int math) {
this.math = math;
}
/**
* 获取
*
* @return english
*/
public int getEnglish() {
return english;
}
/**
* 设置
*
* @param english
*/
public void setEnglish(int english) {
this.english = english;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + ", chinese = " + chinese + ", math = " + math + ", english = " + english + "}";
}
@Override
public int compareTo(Student o) {
/* 按照总分从高到低输出到控制台
如果总分一样,按照语文成绩排
如果语文一样,按照数学成绩排
如果数学成绩一样,按照英语成绩排
如果英文成绩一样,按照年龄排
如果年龄一样,按照姓名的字母顺序排
如果都一样,认为是同一个学生,不存。*/
int sum1 = this.getChinese() + this.getMath() + this.getEnglish();
int sum2=o.getChinese() + o.getMath() + o.getEnglish();
int tmp = sum2-sum1;
// 如果总分一样,按照语文成绩排
tmp = tmp == 0 ? this.getChinese() - o.getChinese() : tmp;
// 如果语文一样,按照数学成绩排
tmp = tmp == 0 ? this.getMath() - o.getMath() : tmp;
//如果数学成绩一样,按照英语成绩排
tmp = tmp == 0 ? this.getEnglish() - o.getEnglish() : tmp;
//如果英文成绩一样,按照年龄排
tmp = tmp == 0 ? this.getAge() - o.getAge() : tmp;
// 如果年龄一样,按照姓名的字母顺序排
tmp = tmp == 0 ? this.getName().compareTo(o.getName()) : tmp;
return tmp;
}
}