Set
HashSet初探
Set<String> strs = new HashSet<>();
strs.add("asdf");
strs.add("asdf");
strs.add("asdf");
strs.add("asdf");
strs.add("fdas");
strs.add("jmjm");
strs.add("jnjn");
for (String str : strs) {
System.out.println(str);
}
/*
jmjm
asdf
jnjn
fdas
*/
- 存储时的顺序和取出的顺序不同
- 不可重复
- 放到HashSet集合中的元素实际上是放到HashSet集合的key部分了。
TreeSet初探
二叉树
TreeSet集合存储元素的特点:
无序不可重复,但是存储的元素可以自动按照大小顺序排序!称为可排序集合
无序代表没有下标并不是指顺序
//创建集合
Set<String> set = new TreeSet<>();
//添加元素
set.add("A");
set.add("F");
set.add("B");
set.add("V");
set.add("C");
for (String s : set) {
System.out.println(s);
}
/*
A
B
C
F
V
*/
- TreeSet集合底层实际上是一个TreeMap
- TreeMap集合底层是一个二叉树。
- 放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了
- TreeSet集合中的元素:无序不可重复,但是可以按照元素的大小自动排序。称为:可排序集合。
数据库中有很多数据:
userId name birth
1 zs 1980-11-11
2 ls 1980-10-11
3 ww 1981-11-11
4 zl 1979-11-11
编写程序从数据库当中取出数据,在页面展示用户信息的时候按照生日升序或者降序。这个时候可以使用TreeSet集合,因为TreeSet集合放进去,拿出来就是有顺序的。
//创建一个TreeSet集合
TreeSet<String> ts = new TreeSet<>();
//添加String
ts.add("zhangsan");
ts.add("lisi");
ts.add("wangwu");
ts.add("zhangsi");
ts.add("wangliu");
//遍历
for (String t : ts) {
//按照字典顺序升序
System.out.println(t);
}
/*
lisi
wangliu
wangwu
zhangsan
zhangsi
*/
//创建第二个TreeSet
TreeSet<Integer> ts2 = new TreeSet<>();
//添加元素
ts2.add(100);
ts2.add(200);
ts2.add(300);
ts2.add(400);
ts2.add(5400);
ts2.add(70);
ts2.add(52);
ts2.add(1733);
//遍历
for (Integer integer : ts2) {
System.out.println(integer);
}
//升序排列
/*
52
70
100
200
300
400
1733
5400
*/
自定义类型排序
对于自定义的对象与类型 ,TreeSet无法排序
以下程序对于Person类型来说,无法排序。因为没有指定Person对象之间的比较规则。
谁大谁小没有一个比较的规则。
会出现错误:java.lang.ClassCastException:
com.javase.collection.Set.Person cannot be cast to java.lang.Comparable
public class TreeSetTest03 {
public static void main(String[] args) {
//创建对象
Person p1 = new Person(12);
Person p2 = new Person(54);
Person p3 = new Person(20);
Person p4 = new Person(25);
//创建TreeSet集合
TreeSet<Person> people = new TreeSet<>();
//添加元素
people.add(p1);
people.add(p2);
people.add(p3);
people.add(p4);
//遍历
for (Person person : people) {
System.out.println(person);
}
}
}
class Person{
private int age;
//构造方法
public Person() {
}
public Person(int age) {
this.age = age;
}
//重写toString方法
@Override
public String toString() {
return "Person{" +
"age=" + age +
'}';
}
}
自定义规则
第一种方法:类继承Comparable接口
public class TreeSetTest04 {
public static void main(String[] args) {
//创建对象
Customer c1 = new Customer(12);
Customer c2 = new Customer(54);
Customer c3 = new Customer(20);
Customer c4 = new Customer(25);
//创建TreeSet集合
TreeSet<Customer> customers = new TreeSet<>();
//添加元素
customers.add(c1);
customers.add(c2);
customers.add(c3);
customers.add(c4);
//遍历
for (Customer customer : customers) {
System.out.println(customer);
}
}
}
//放在TreeSet集合中的元素需要实现java.lang.Comparable接口。
//并且实现compareTo方法。equals可以不写
class Customer implements Comparable<Customer>{
private int age;
//构造方法
public Customer() {
}
public Customer(int age) {
this.age = age;
}
//需要在这个方法中编写比较的逻辑,或者说比较的规则
//k.compareTo(t.key)
//拿着参数k和集合中每一个k进行比较,返回值可能是 >0 <0 ==0
//比较规则最终还是由程序员指定的:例如按照年龄升序。或者按照年龄降序。
@Override
public int compareTo(Customer c) { // c1.compareTo(c2)
//this是c1
//c是c2
//c1和c2比较的时候,就是this和c比较
/*int age1 = this.age;
int age2 = c.age;
if (age1 == age2){
return 0;
}else if(age1 > age2) {
return 1;
}else {
return -1;
}*/
return this.age - c.age; //>0 <0 ==0
}
//重写toString方法
@Override
public String toString() {
return "Customer{" +
"age=" + age +
'}';
}
}
//创建对象
Dog d1 = new Dog("小汪",7);
Dog d2 = new Dog("小花",7);
Dog d3 = new Dog("张三",8);
Dog d4 = new Dog("小安",55);
//创建集合
TreeSet<Dog> dogs = new TreeSet<>();
//添加元素
dogs.add(d4);
dogs.add(d2);
dogs.add(d1);
dogs.add(d3);
//遍历
for (Dog dog : dogs) {
System.out.println(dog);
}
public class Dog implements Comparable<Dog>{
private String name;
private int age;
public Dog() {
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Dog o) {
//年龄相同按照名字排序
//姓名是String类型,可以直接比较。调用compareto来完成比较
if(this.age == o.age) return this.name.compareTo(o.name);
//年龄不一样
return this.age - o.age;
}
}
第二种方法:使用比较器
Cat对象:
public class Cat {
private String name;
private int age;
//构造方法
public Cat() {
}
public Cat(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;
}
//重写toString方法
@Override
public String toString() {
return "小猫咪[" +
"name='" + name + '\'' +
", age=" + age +
']';
}
}
比较器对象:
/*
单独在这里编写一个比较器
比较器实现java.util.Comparator接口。(Comparable是java.lang包下的。Comparator是java.util包下的)
*/
public class CatComparator implements Comparator<Cat> {
@Override
public int compare(Cat o1, Cat o2) {
if (o1.getAge() == o2.getAge())
return o1.getName().compareTo(o2.getName());
return o1.getAge() - o2.getAge();
}
}
测试类:
public class TreeSetTest06 {
public static void main(String[] args) {
//创建TreeSet,需要使用这个比较器。
//TreeSet<Cat> cats = new TreeSet<>(); //这种方式不行 需要在构造方法传递一个比较器进去。创建TreeSet
TreeSet<Cat> cats = new TreeSet<>(new CatComparator());
//添加对象
cats.add(new Cat("小沫",15));
cats.add(new Cat("小幂",7));
cats.add(new Cat("小咪",7));
cats.add(new Cat("小幂",8));
cats.add(new Cat("小米",7));
cats.add(new Cat("小米",9));
//遍历
for (Cat cat : cats) {
System.out.println(cat);
}
}
}
/*
小猫咪[name='小咪', age=7]
小猫咪[name='小幂', age=7]
小猫咪[name='小米', age=7]
小猫咪[name='小幂', age=8]
小猫咪[name='小米', age=9]
小猫咪[name='小沫', age=15]
*/
使用匿名内部类
TreeSet<Cat> cats = new TreeSet<>(new Comparator<Cat>() {
@Override
public int compare(Cat o1, Cat o2) {
if (o1.getAge() == o2.getAge())
return o1.getName().compareTo(o2.getName());
return o1.getAge() - o2.getAge();
}
} );
降序输出
通过传入比较器让树降序输出
public class TreeSetTest03 {
public static void main(String[] args) {
//创建集合
Set<Integer> set = new TreeSet<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
//添加元素
set.add(15);
set.add(17);
set.add(188);
set.add(173);
//遍历
for (Integer integer : set) {
System.out.println(integer);
}
}
}
总结
Comparable和comparator怎么选择?
当比较规则一成不变的时候,或者说当比较规则只有一个的时候,建议实现Comparable接口
如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口。
Comparator接口的设计符合OCP原则
OCP原则:软件实体应该对扩展开放,对修改关闭,其含义是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。