Set集合
Set集合概述
- Set集合与Collection集合中的方法一模一样
HashSet
一.概述
- HashSet实现Set接口,由哈希表支持。它不保证set的迭代顺序,特别是它不保证该顺序恒久不变。此类允许使用null
二.HashSet存储字符串遍历
import java.util.HashSet;
public class Demo1_HashSet {
public static void main(String[] args) {
HashSet<String> hs = new HashSet<>(); //创建HashSet对象
boolean b1 = hs.add("a");
boolean b2 = hs.add("a"); //当向set集合中存储重复元素的时候返回false
hs.add("b");
hs.add("c");
hs.add("d");
System.out.println(hs); //HashSet的继承体系中有重写方法toString
System.out.println(b1);//true
System.out.println(b2);//false
for (String string : hs) {
System.out.println(string); //只要能用迭代器迭代的就可以用增强for循环
//a b c d
}
}
}
三.HashSet存储自定义对象保证元素唯一性
一.HashSet原理
1.我们使用Set集合都是需要去除重复元素的,如果在存储的时候逐个进行equals()比较,效率较低,哈希算法提高了去重复的效率,降低了equals()方法的使用次数
2.当HashSet调用add()方法存储对象的时候,先调用对象的hashCode()方法得到一个哈希值,然后在集合中查找是否有哈希值相同的对象
- 如果没有哈希值相同的对象就直接存入集合
- 如果有哈希值相同的对象,就和哈希值相同的对象进行equals()比较,比较结果为false就存入,true则不存
二.将自定义类的对象存入HashSet去重复
- 类中必须要重写hashCode()和equals方法
- hashCode():属性相同的对象返回值必须相同,属性不同的返回值尽量不同(提高效率)
- equals():属性相同返回true,不同返回false,返回false的时候存储
案例演示
public class Person {
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
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 "Person [name=" + name + ", age=" + age + "]";
}
/*
代码优化:为了减少调用equals方法比较,优化hashCode代码写法
最终版就是自动生成的
*/
@Override
public boolean equals(Object obj) {
if (this == obj) //调用的对象和传入的对象是同一个对象
return true; //直接返回true
if (obj == null) //传入对象为null
return false; //返回false
if (getClass() != obj.getClass()) //判断两个字节码文件是否为同一个字节码文件
return false; //如果不是直接返回false
Person other = (Person)obj; //向下转型
if (age != other.age) //如果调用对象的年龄不等于传入对象的年龄
return false; //返回false
if (name == null) { //如果调用对象的姓名为null
if (other.name != null) //传入对象的姓名也为null
return false; //返回false
} else if (!name.equals(other.name))//调用对象的姓名不等于传入对象的姓名
return false; //返回false
return true; //如果相等返回true
/*System.out.println("执行了吗");
Person p = (Person)obj;
return this.name.equals(p.name) && this.age == p.age;
*/
}
/*
为什么是31?
1、31是质数,质数是能被1和自己本身整除的数、
2、31这个数既不大也不小
3、31这个数好算,2的五次方-1,2向左移动五位
*/
@Override
public int hashCode() {
//return 10; //返回常量码值,每个对象码值都相同,需要频繁调用equals方法,速度慢
//为了减少equals方法的使用,应该都返回不同的码值,所以就有了hashCode方法
/*
final int NUM = 38;
return name.hashCode() * NUM + age;//根据属性返回不同的码值,降低重复几率,减少equals方法的调用
*/
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
}
import java.util.HashSet;
import com.heima.bean.Person;
public class Demo1_HashSet {
public static void main(String[] args) {
HashSet<Person> hs = new HashSet<>();
hs.add(new Person("张三",23));
hs.add(new Person("张三",23));
hs.add(new Person("李四",23));
hs.add(new Person("李四",23));
hs.add(new Person("李四",23));
hs.add(new Person("李四",23));
System.out.println(hs);
}
}
LinkedHashSet
- 底层是由链表实现的,是Set集合中唯一一个能保证怎么存就怎么取得集合对象
- 因为是HashSet的子类,所以也是保证元素唯一的,与HashSet的原理一样
案例演示
import java.util.LinkedHashSet;
public class Demo2_LinkedHashSet {
public static void main(String[] args) {
LinkedHashSet<String> lhs = new LinkedHashSet<>();
lhs.add("a");
lhs.add("a");
lhs.add("a");
lhs.add("b");
lhs.add("c");
lhs.add("d");
System.out.println(lhs);
}
}
TreeSet
一.TreeSet集合概述
- TreeSet集合是用来对元素进行排序的,同样他也可以保证元素的唯一
import java.util.TreeSet;
public class Demo3_TreeSet {
public static void main(String[] args) {
TreeSet<Integer> ts = new TreeSet<>();
ts.add(3);
ts.add(1);
ts.add(1);
ts.add(2);
ts.add(2);
ts.add(3);
ts.add(3);
System.out.println(ts);//123
}
}
二.TreeSet集合保证元素唯一性和自然排序的原理
- 当compareTo方法返回0时集合只有一个元素
- 当compareTo方法返回正数时集合怎么存就怎么取
- 当compareTo方法返回负数时集合会倒序存储
案例演示
public class Person implements Comparable<Person>{//实现Comparable接口,才能重写compareTo方法
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
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 "Person [name=" + name + ", age=" + age + "]";
}
/*//按年龄排序
@Override
public int compareTo(Person o) {
int num = this.age - o.age;
return num == 0 ? this.name.compareTo(o.name) : num;
}*/
/*//按姓名排序,其实是Unicode码表
@Override
public int compareTo(Person o) {
int num = this.name.compareTo(o.name); //姓名是主要条件
return num == 0 ? this.age - o.age : num; //年龄是次要条件
}*/
//按姓名长度排序
@Override
public int compareTo(Person 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;
}
}
import java.util.TreeSet;
import com.heima.bean.Person;
public class Demo3_TreeSet {
public static void main(String[] args) {
TreeSet<Person> ts = new TreeSet<>();
ts.add(new Person("张三",23));
ts.add(new Person("李四",13));
ts.add(new Person("王五",43));
ts.add(new Person("赵六",33));
System.out.println(ts);
}
}
三.比较器排序
- 实现Comparator接口,接口中有compare方法,重写此方法进行比较
- TreeSet构造方法传入Comparator的子类对象
class CompareBylen implements Comparator<String> {//实现Comparator接口,接口中有compare方法,重写此方法
@Override
public int compare(String s1, String s2) {
int num = s1.length() - s2.length(); //长度是主要条件
return num == 0 ? s1.compareTo(s2) : num; //内容是次要条件
}
}
import java.util.Comparator;
import java.util.TreeSet;
import com.heima.bean.Person;
public class Demo3_TreeSet {
public static void main(String[] args) {
//demo1();
//demo2();
//demo3();
//demo4();
//需求:把字符串按照长度排序
TreeSet<String> ts = new TreeSet<>(new CompareBylen());//构造方法//Comparator c = new CompareByLen();
ts.add("aaaaaaaa");
ts.add("z");
ts.add("wc");
ts.add("nba");
ts.add("cba");
System.out.println(ts);//[z, wc, cba, nba, aaaaaaaa]
}
}
四.TreeSet原理
A.TreeSet是用来排序的,可以指定一个顺序,对象存入之后会按照指定的顺序排序
B.使用方法
a: 自然顺序(Comparable)
- TreeSet类的add()方法会把存入的对象提升为Comparable类型
- 调用对象的compareTo()方法和集合中的对象比较
- 根据compareTo()方法返回的结果进行储存
b: 比较器顺序(Comparator)
- 创建TreeSet的时候可以指定一个Comparator
- 如果传入了Comparator的子类对象,那么TreeSet就会按照比较器中的顺序排序
- add()方法内部会自动调用Comparator接口中的compare()方法排序
- 调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数
c: 两种方法的区别
- TreeSet构造函数什么都不传,默认按照类中的Comparable的顺序(没有就报错ClassCastException)
- TreeSet如果传入Comparator,就优先按照Comparator