【集合-定制排序】详解总结自然排序compareTo+比较器排序compare+两种定制排序的区别,用法+TreeSet,TreeMap存储定制排序的类对象
目录
❤ 概括典例:分别使用Comparable和Comparator分别给Person类对象排序,一个升序一个降序。
- 当主属性重复时,再用次属性排序。
如果两个对象的两个属性都相同认为对象重复,对于List就按添加顺序排序先后,Set默认重复不可存储进Set下集合
A:-----------------------------------------------------------------------------------------------------------
需要重写 Comparable的compareTo(), Comparator的compara()方法。因为set集合存储数据判断是否重复就是用这两个方法。为了方便查看,重写toString()方法(可以alt+insert自动生成,写好类的属性一路next即可)
自然排序:姓名降序,姓名相同则年龄升序。
public class Demo31_Person implements Comparable<Demo31_Person>{
String name;
int age;
int id;
public Demo31_Person(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
/** ● 姓名降序,姓名相同时年龄升序排序
*/
@Override
public int compareTo(Demo31_Person o) {
//类似return o.name - this.name;
if(o.name.compareTo(this.name) == 0){//姓名相同,年龄升序
return this.age - o.age;//年龄升序
}
return o.name.compareTo(this.name);//姓名降序
//分析: 因为comparaTo中 如果this > obj 返回正数。
// 此时o.name.compareTo(this.name)方法中的this是o.name,obj是this.name
// 如果o.name更大,方法返回正数
// 此方法return这个正数,默认新添加的元素o(比较器中新的是o2)排在前,
// 则是o更大排在前,完成降序
}
@Override
public String toString() {
return "{name='" + name + '\'' +
", age=" + age + '\'' +
", id=" + id +
'}';
}
}
定制排序:年龄降序,年龄相同则姓名升序。
private static void demo31() {
Demo31_Person p1 = new Demo31_Person("aa", 12, 3);
Demo31_Person p2 = new Demo31_Person("aa", 30, 3);
Demo31_Person p3 = new Demo31_Person("ab", 18, 3);
Demo31_Person p4 = new Demo31_Person("ba", 2, 2);
Demo31_Person p5 = new Demo31_Person("bb", 2, 1);
Demo31_Person p6 = new Demo31_Person("bb", 2, 1);
Demo31_Person p7 = new Demo31_Person("bb", 2, 4);
Set<Demo31_Person> set1 = new TreeSet<>();//因为TreeSet,是直接按照排序规则存储
List<Demo31_Person> list1 = new ArrayList<Demo31_Person>();//按照添加顺序存储
Collections.addAll(set1, p1, p2, p3, p4, p5, p6, p7);
Collections.addAll(list1, p1, p2, p3, p4, p5, p6, p7);
System.out.println("TreeSet: " + set1);
System.out.println("ArrayList:" + list1);
Collections.sort(list1, new Comparator<Demo31_Person>() {
@Override
public int compare(Demo31_Person o1, Demo31_Person o2) {
if(o1.age == o2.age){//年龄相同,姓名升序
return o1.name.compareTo(o2.name);//年龄升序
}
return o2.age - o1.age;//年龄降序
}
});//排序一下,参数只接收List接口下对象
System.out.println("排序后ArrayList:" + list1);
}
结果
TreeSet: [{name='bb', age=2', id=1}, {name='ba', age=2', id=2}, {name='ab', age=18', id=3}, {name='aa', age=12', id=3}, {name='aa', age=30', id=3}]
ArrayList:[{name='aa', age=12', id=3}, {name='aa', age=30', id=3}, {name='ab', age=18', id=3}, {name='ba', age=2', id=2}, {name='bb', age=2', id=1}, {name='bb', age=2', id=1}, {name='bb', age=2', id=4}]
排序后ArrayList:[{name='aa', age=30', id=3}, {name='ab', age=18', id=3}, {name='aa', age=12', id=3}, {name='ba', age=2', id=2}, {name='bb', age=2', id=1}, {name='bb', age=2', id=1}, {name='bb', age=2', id=4}]
Disconnected from the target VM, address: '127.0.0.1:29998', transport: 'socket'
1 两种接口对应比较方法的参数含义及原理
简记:
旧(this,o1)- 新(o,o2),旧.comparaTo(新)-> 升序
反之降序
原理:
继承接口Comparable< E >实现方法compareTo(E o)
继承接口Comparator< E >实现方法compare(E o1, E o2)中
比较大小时,o(o2)代表当前准备添加进来需要排序的新元素,this(o1)代表集合中所有已经排序完成的旧元素。
1.方法内 this - o 为何升序
-
return 1 时,排序默认新在前,即o2,o在前;
所以this-o如果返回正数,说明o小,o还排在前,完成升序排序。
o-this返回正数,说明this小,this还在后,完成降序排序。 -
return -1 时,反之。
-
return 0 时,默认this(o1)等于o(o2)
2. ♥ 特殊情况:o.comparaTo(this) 为何降序
- 这个方法本身是默认升序,但此时在comparaTo方法内部外部的o为this,因为是o作为调用的对象,外部的this为内部的o
- 内部如果 this > o ,是返回正数。说明外部 o > this,但返回正数时,新的o会排在前。完成降序
理解原理的记:
返回正数,代表新添加的 o2(o)排在前;
返回负数,代表已存在的 o1(this)排在前;
2 compareTo自然排序定制
2.1 用法一:直接用于Number对象(基本数据类型的包装类)和String对象




//↓ main中执行
//通过compareTo比较引用类型String类对象的大小
System.out.println("-------------String-------------");
String str1 = "1234y";
System.out.println("sytudf".compareTo("syt"));//3
System.out.println(str1.compareTo("1234y45ttyu"));//-6
//通过compareTo比较基本数据类型的包装类大小
//Byte字节型1字节
//Byte只能用字符串参数装箱,且无法识别整型数据为Byte必须装箱
System.out.println("-------------Byte-------------");
System.out.println(new Byte("12").compareTo(new Byte("124")));//-112
//Short短整型2字节
//Short只能用字符串参数装箱,且无法识别整型数据为Short必须装箱。用数据装箱时需要强转。
System.out.println("-------------Short-------------");
System.out.println(new Short((short)45).compareTo((short)56));//-11
System.out.println(new Short("235").compareTo(new Short("345")));//-110
//Integer整型4字节
//Integer可以用值参数装箱,也可以用字符串参数装箱;且值参数自动识别整数数据为int作为参数自动装箱
System.out.println("-------------Integer-------------");
System.out.println(new Integer(45).compareTo(56));//-1
System.out.println(new Integer("3456").compareTo(new Integer("345")));//1
//Long长整型8字节,定义时尾部加L或l
//Long可以用值参数装箱,也可以用字符串参数装箱,用字符串参数时不加L,l;且值参数自动识别整数l数据为Long作为参数自动装箱
System.out.println("-------------Long-------------");
System.out.println(new Long(7845634353543l).compareTo(7856l));//1
System.out.println(new Long("45634").compareTo(34545L));//1;注意:用字符串参数就不用加L
//Float单精度浮点型(小数型)4字节,定义时尾部加f或F
//Float可以用值参数装箱,也可以用字符串参数装箱,用字符串参数时不加F,f;且值参数自动识别小数F,f数据为Float作为参数自动装箱
System.out.println("-------------Float-------------");
System.out.println(new Float(34.67f).compareTo(34.62F));//1
System.out.println(new Float("35.6").compareTo(35.6f));//0
//Double双精度浮点型(小数型)8字节,小数默认是double
//Double可以用值参数装箱,也可以用字符串参数装箱;且值参数自动识别小数数据为Double作为参数自动装箱
System.out.println("-------------Double-------------");
System.out.println(new Double(3456.786).compareTo(3456.756));
System.out.println(new Double("654.345").compareTo(645.345));
//Character字符型,2字节。ASCII码中,1个英文字符占用1个字节,1个汉字字符占用2个字节的空间
//Character可以用值参数装箱,不可以用字符串参数装箱;且值参数自动识别'一个字符'数据为Chacter作为参数自动装箱
System.out.println("-------------Character-------------");
System.out.println(new Character('y').compareTo(new Character('y')));//0
System.out.println(new Character('8').compareTo('9'));//-1
System.out.println(new Character('1').compareTo('A'));//-16
//Boolean布尔型1字节
//Boolean可以用值参数装箱,可以用字符串参数装箱;且值参数自动识别true或false数据为Boolean作为参数自动装箱
System.out.println("-------------Boolean-------------");
System.out.println(new Boolean(false).compareTo(true));//-1
System.out.println(new Boolean("true").compareTo(new Boolean("true")));//0

关于调用比较先后
- 关于包装类装箱
- Byte和Short对于做方法的参数只能用字符串参数可以直接装箱,用值做参数需强转。即compareTo(参数)参数必须(“字符串”),((byte)整数), ((short)整数)才可以自动装箱。
- 而其他所有基本数据类型和String 做compareTo参数时,都是自动装箱,不用new 包装类
- Character只能用值参数,不能用字符串参数装箱。其他7种基本数据类型和String都可以用字符串做参数装箱
- 其他7种(除char以外的基本数据类型包装类+String类既可以用值参数装箱,也可以用字符串参数手动装箱。可以通过数据值在方法参数部分自动装箱
2.2 用法二 : 自定义类中继承Comparable接口重写compareTo方法以定制自定义类的排序规则
- Student类,以属性id决定大小,实现自然排序
- 继承接口Comparable实现方法 public int compareTo(Student o)
public class Student implements Comparable<Student>{
//学生,三个属性,姓名,年龄,学号
private String name;
private int age;
private int id;
public Student( int id,String name, int age){
this.name = name;
this.age = age;
this.id = id;
}
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return this.age;
}
public void setAge(int age){
this.age = age;
}
public int getId(){
return this.id;
}
//学号不会重复,所以以学号来决定大小
@Override
public int compareTo(Student o) {//o代表当前添加进来需要排序的元素,this代表集合中所有已经排序完成的元素
if(this.id > o.id){
return 1;//默认this大于o,o在前,this在后
}else if(this.id < o.id){
return -1;
}else{
return 0;
}
//return 0;
}
@Override
public String toString() {
//return super.toString();
return getClass().getName()+"[id="+id+",name="+name+",age="+age+"]";
}
}
//Student类继承接口Comparable<E>,实现方法compareTo(E o)
Student stu1 = new Student(2026,"学生1",15);
Student stu2 = new Student(2022,"学生2",23);
Student stu3 = new Student(2020,"学生3",35);
//1.自然排序的自定义类Student
//TreeSet
Set<Student> set1 = new TreeSet<>();
set1.add(stu1);
set1.add(stu2);
set1.add(stu3);
System.out.println(set1);
//TreeMap
Map<Student,Integer> map1 = new TreeMap<>();
map1.put(stu1,23);
map1.put(stu2,24);
map1.put(stu3,45);
System.out.println(map1);
- 运行结果如下
实现自然排序自定义类的TreeSet[com.qx.javaTest.Student[id=2020,name=学生3,age=35], com.qx.javaTest.Student[id=2022,name=学生2,age=23], com.qx.javaTest.Student[id=2026,name=学生1,age=15]]
实现自然排序自定义类的TreeMap{com.qx.javaTest.Student[id=2020,name=学生3,age=35]=45, com.qx.javaTest.Student[id=2022,name=学生2,age=23]=24, com.qx.javaTest.Student[id=2026,name=学生1,age=15]=23}
3 compare比较器排序
3.1 用法一:在自定义类中继承Comparator<类名>重写compare方法(和自然排序一样),集合参数new 自定义类对象
用于普通类,基本什么类中都可以这样用写
- MyData类,以属性data1决定大小,实现比较器
- 继承接口
Comparator<MyData>,实现方法public int compare(MyData o1, MyData o2)等于是自己给自己写排序,其他类继承Comparator<MyData>也可以给MyData写排序方法
Ctrl + I: 实现接口方法
Ctrl + O: 重写父类方法
package com.qx.day10;
import java.util.Comparator;
public class MyData implements Comparator<MyData> {
public MyData(){
}
public MyData(int data1,String name, int data2){
this.data1 = data1;
this.name = name;
this.data2 = data2;
}
private String name;
private int data1;
private int data2;
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setData1(int data1){
this.data1 = data1;
}
public int getData1(){
return data1;
}
public void setData2(int data2){
this.data2 = data2;
}
public int getData2(){
return data2;
}
@Override
public int compare(MyData o1, MyData o2) {//理解新添加需要排序的元素是o1,已经排好序的元素s为o2
if(o1.data2 > o2.data2){
return 1;//默认,o1>o2,o2在前,o1在后
}else if(o1.data2 < o2.data2){
return -1;////默认,o1<o2,o1在前,o2在后
}
else{
return 0;//相等
}
//添加顺序1,3,2,4.
//return 0;//1
//return 1;//1,3,2,4
//return -1;//4,2,3,1
//返回-1,默认o1<o2,o1排在o2之前
}
@Override
public String toString() {
//return super.toString();
return getClass().getName()+"[data1="+data1+",name="+name+",data2="+data2+"]";
}
}
//MyData类继承接口Comparator<E>,实现方法compare(E o1, E o2)
MyData d1 = new MyData(34,"数据1",56);
MyData d2 = new MyData(45,"数据2",6778);
MyData d3 = new MyData(23,"数据3",908);
- 比较器排序的自定义类MyData,比较器类型的自定义类需要在new集合的时候提供该类对象作为参数
Set<MyData> set2 = new TreeSet<>(new MyData());即可根据自定义方法自动排序
//2.比较器排序的自定义类MyData,比较器类型的自定义类需要在new集合的时候提供该类对象作为参数
//TreeSet
Set<MyData> set2 = new TreeSet<>(new MyData());
set2.add(d1);
set2.add(d2);
set2.add(d3);
System.out.println("实现比较器排序的自定义类的TreeSet"+set2);
//TreeMap
Map<MyData,Student> map2 = new TreeMap<>(new MyData());
map2.put(d1,stu1);
map2.put(d2,stu2);
map2.put(d3,stu3);
System.out.println("实现比较器排序的自定义类的TreeMap"+map2);
- 运行结果
实现比较器排序的自定义类的TreeSet[com.qx.day10.MyData[data1=34,name=数据1,data2=56], com.qx.day10.MyData[data1=23,name=数据3,data2=908], com.qx.day10.MyData[data1=45,name=数据2,data2=6778]]
实现比较器排序的自定义类的TreeMap{com.qx.day10.MyData[data1=34,name=数据1,data2=56]=com.qx.javaTest.Student[id=2026,name=学生1,age=15], com.qx.day10.MyData[data1=23,name=数据3,data2=908]=com.qx.javaTest.Student[id=2020,name=学生3,age=35], com.qx.day10.MyData[data1=45,name=数据2,data2=6778]=com.qx.javaTest.Student[id=2022,name=学生2,age=23]}
3.2 用法二(特有):普通类(即不在类中继承Comparator接口重写排序方法),而是new存储该类的集合时,提供参数传入匿名内部类,匿名比较器重写排序方法。
用于TreeSet,按照排序规则存储的集合使用,在new集合时参数为new Comparator<该普通类名>(){重写比较方法compare}
-
- Puppy类
- 普通类,不实现接口
public class Puppy {
private int id;
private String name;
public Puppy(int id,String name){
this.name = name;
this.id = id;
}
public int getId(){
return this.id;
}
public void setId(int id){
this.id = id;
}
public String getNmae(){
return this.name;
}
public void setName(String name){
this.name = name;
}
@Override
public String toString() {
//return super.toString();
return getClass().getName()+"[id="+id+",name="+name+"]";
}
}
//Puppy类,普通类
Puppy p1 = new Puppy(8,"边牧");
Puppy p2 = new Puppy(4,"伯恩山");
Puppy p3 = new Puppy(3,"萨摩耶");
- 普通类Puppy,在对应集合new的时候参数传入匿名类重写比较器。
//普通类Puppy,在集合new的时候传入匿名类实现普通自定义类的定制排序方法
//TreeSet
//自然排序不能这样写,因为本身就是重写在自定义类中直接使用,不需要传参
// Set<Puppy> set3 = new TreeSet<>(new Comparable<Puppy>(){
// @Override
// public int compareTo(Puppy o) {
// //return 0;
//
// }
// });
Set<Puppy> set3 = new TreeSet<Puppy>(new Comparator<Puppy>(){
@Override
public int compare(Puppy o1, Puppy o2) {
//return 0;
if(o1.getId() > o2.getId()){
return 1;
}else if(o1.getId() < o2.getId()){
return -1;
}else{
return 0;
}
}
});
set3.add(p1);
set3.add(p2);
set3.add(p3);
System.out.println("普通自定义类在new TreeMap时传匿名对象重写比较器"+set3);
//TreeMap,key和value都是自定义类的情况,
Map<Puppy,MyData> map3 = new TreeMap<>(new Comparator<Puppy>(){
@Override
public int compare(Puppy o1, Puppy o2) {
//return 0;
if(o1.getId() > o2.getId()){
return -1;
}else if(o1.getId() < o2.getId()){
return 1;
}else{
return 0;
}
}
});
map3.put(p1,d1);
map3.put(p2,d2);
map3.put(p3,d3);
System.out.println("普通自定义类在new TreeMap时传匿名对象重写比较器"+map3);
普通自定义类在new TreeMap时传匿名对象重写比较器[com.qx.javaTest.Puppy[id=3,name=萨摩耶], com.qx.javaTest.Puppy[id=4,name=伯恩山], com.qx.javaTest.Puppy[id=8,name=边牧]]
普通自定义类在new TreeMap时传匿名对象重写比较器{com.qx.javaTest.Puppy[id=8,name=边牧]=com.qx.day10.MyData[data1=34,name=数据1,data2=56], com.qx.javaTest.Puppy[id=4,name=伯恩山]=com.qx.day10.MyData[data1=45,name=数据2,data2=6778], com.qx.javaTest.Puppy[id=3,name=萨摩耶]=com.qx.day10.MyData[data1=23,name=数据3,data2=908]}
3.3 用法三(特有):在Collections类的方法public static void sort(List list,Comparator<? super T> )中传入比较器做第二个参数,重写排序规则
用于List接口下类对象
- 改变字符串的比较方式,按首位字母降序排序,只根据首位字符排序,首位相同就保持稳定性
//稳定性:例如 ab,ba,aa 排序后应该是 ab,aa,ba,因为只根据首位排序,
// ab和aa相对位置不变,ab依然在aa前就叫稳定性
List<String> list1 = new ArrayList<>();
Collections.addAll(list1, "aa", "", "baa", "ab", "ca", "");
System.out.println("list1:" + list1);//list1:[aa, , baa, ab, ca, ]
//因为ArrayList中可以存储null,但null不可参与排序,见demo28
//然后空字符串默认最小,因为降序所以排在最后
Collections.sort(list1, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
if(null == o1 || null == o2){
//注意,为了不处理异常,只是这里抛出一次,所以只能写运行时异常
throw new NullPointerException("null可以作为元素存储在List下集合中,但不可以参与排序");
}else if(0 == o1.length() && 0 == o2.length()){
return 0;
}
else if(0 == o1.length()){//空字符串最小,降序排得在最后面。因为返回正数数时,新添加的o2排在前
return 1;
}else if(0 == o2.length()){//因为返回负数时,新添加的o2排在后
return -1;
}
char ch1 = o1.charAt(0);
char ch2 = o2.charAt(0);
if(ch1 == ch2){
return 0;
}
return ch2 - ch1;
//(● 牢记一句即可,返回正数时,方法会把新添加的o2(o)排在前,已存在的o1(this)在后 ●)
// 所以首字母降序排序的需求下
// 情况1:ch1 < ch2
// 代表 o1 < o2,为了降序,小的o1得排在后。而返回正数的情况,新o2(o)在前。ch2 - ch1为正数,符合降序
// 情况2:ch2 < ch1
// 此处ch2小代表o2小,小的o2需要排在后。返回负数时,旧o1(this)在前。ch2 - ch1 为负数,符合降序
}
});
System.out.println(list1);//[ca, baa, aa, ab, , ]
4 自然排序和比较器排序的区别总结
- 自然排序本身可以用作基本数据类型的包装类(如Character,);比较器不可以
- 重写自然排序的类必须①在自定类中重写compareTo方法。重写比较器的类除①之外还可以,②new集合的时候参数提供对象,该对象包含此类的比较器③Collections.sort时传入重写好的比较器对象。都是new Comparator<对象名> 匿名内部类直接重写compare方法)
- 自然排序除了直接使用包装类和String外,不可以改变这部分源码。但比较器可以通过传入重写规则的比较器更改包装类,String的排序规则。
- 比较器参数o1表示集合中已经排序完成的所有元素,o2表示当前新添加的一个;自然排序参数,对象自身this表示集合中已经排序完成的所有元素,o表示当前新添加的一个
5 自由改写理解定制排序返回值含义
- 假设方法始终只return 1 ,表示的含义则为新添加的o(o2)始终更大,排在后。集合为按照元素的添加顺序存储
- 假设方法只 return 0,表示所有添加进来的元素都相同,则只有集合为空时能存储一个元素。因为TreeSet和TreeMap的key都不可重复
- 假设方法只 return -1 ,表示的含义则为新添加的o(o2)始终更小,排在前。集合为按照元素的添加顺序的逆序存储
练习复习
1 Integer类,自定义Student类 定制比较器comparator排序规则
//学生类:三个属性
public static class Student {
String name;
int id;
int age;
public Student(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
}
//排序Integer类
//降序
public static class MyComp implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}
//打印数组
public static void printArray(Integer[] arr){
for(Integer i : arr){
System.out.print(i+",");
}
System.out.println();
}
//排序Student类
//根据id从小到大,若id同,按年龄从大到小
public static class IdShengAgeJiangOrder implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.id != o2.id ? (o1.id - o2.id) : (o2.age - o1.age);
}
}
//排序Student类
//id升序
public static class IdAscending implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.id - o2.id;
}
}
//排序Student类
//id降序
public static class IdDescending implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o2.id - o1.id;
}
}
//打印学生三个属性
public static void printStudents(Student[] students){
for(Student s : students){
System.out.println("Name:"+s.name+" Id:"+s.id+" Age:"+s.age);
}
}
2 数组[]排序Array.sort
public static void main(String[] args) {
//Integer数组用MyComp排序并打印
Integer[] arr = { 1, 2, 3, 5, 2, 1 };
Arrays.sort(arr, new MyComp());
printArray(arr);
System.out.println("第一条打印");
Student[] stus = {stu1, stu2, stu3, stu4, stu5};
Arrays.sort(stus, new IdShengAgeJiangOrder());
printStudents(stus);
}
结果:
5,3,2,2,1,1,
第一条打印
Name:E Id:3 Age:25
Name:D Id:3 Age:4
Name:B Id:4 Age:20
Name:A Id:4 Age:18
Name:C Id:4 Age:8
3 ArrayList中自定义排序规则
两种调用排序的方法 1、list.sort(比较器); 2、Collections.sort(list, 比较器);
public static void main(String[] args) {
Student stu1 = new Student("A",4,18);
Student stu2 = new Student("B",4,20);
Student stu3 = new Student("C",4,8);
Student stu4 = new Student("D",3, 4);
Student stu5 = new Student("E",3, 25);
//---------------------------------------------------------------
System.out.println("第二条打印");
ArrayList<Student> stus2 = new ArrayList<>();
stus2.add(stu1);
stus2.add(stu2);
stus2.add(stu3);
stus2.add(stu4);
stus2.add(stu5);
//5个Student的ArrayList用.sort(比较器)排序
/**ArrayList两种调用排序写法-------------------------------------------------*/
//ArrayList,两种排序都可以,本身是按照添加顺序存储
stus2.sort(new IdShengAgeJiangOrder());
//♥ Collections.sort(stus2, new IdShengAgeJiangOrder());
for(int i = 0; i < stus2.size(); i++){
Student s = stus2.get(i);
System.out.println(s.name+","+s.id+","+s.age);
}
}
}
运行结果:
第二条打印
E,3,25
D,3,4
B,4,20
A,4,18
C,4,8
4 TreeMap 自定义排序规则
三种排序写法
1、new TreeMap参数用lambda表达式代替比较器
2、TreeMap参数new含比较器的类对象
3、 TreeMap参数new含比较器的匿名类对象(new Comparator (){实现compare方法});
System.out.println("第三条打印");
stu1 = new Student("A", 4, 40);
stu2 = new Student("B", 4, 18);
stu3 = new Student("C", 4, 34);
stu4 = new Student("D", 4, 89);
stu5 = new Student("E", 4, 5);
/**TreeMap的3种排序写法---------------------------------------------------------*/
//5个Student的TreeMap用new时,new时用lambda表达式作为比较器((a,b) -> (a.id - b.id)
//缺点,若id相同则只能存储一个,不能再根据age排序
//♥TreeMap<Student, String> stus3 = new TreeMap<>((a,b) -> (a.id - b.id));
//5个Student的数组,用IdSheng……排序,打印
//♥TreeMap<Student, String> stus3 = new TreeMap<>(new IdShengAgeJiangOrder());
//直接在参数部分实现匿名类重写比较器
TreeMap<Student, String> stus3 = new TreeMap<>(new Comparator<Student> (){
@Override
public int compare(Student o1, Student o2) {
return o1.id != o1.id ? o1.id - o2.id : o1.age - o2.age;
}
});
stus3.put(stu1, "我是学生1,我的名字叫A");
stus3.put(stu2, "我是学生2,我的名字叫B");
stus3.put(stu3, "我是学生3,我的名字叫C");
stus3.put(stu4, "我是学生4,我的名字叫D");
stus3.put(stu5, "我是学生5,我的名字叫E");
for(Student s : stus3.keySet()){
System.out.println(s.name+","+s.id+","+s.age);
}
运行结果:
第三条打印
E,4,5
B,4,18
C,4,34
A,4,40
D,4,89
★TreeSet和HashSet存储重复元素区别
注意! 如果要在Set下集合中添加元素,因为Set中元素不可重复。
- TreeSet或TreeMap判断元素或key是否重复是根据对象的排序规则,如果排序规则认为这两个无大小先后则重复,不可重复存入。即根据类中排序方法compareTo()或compare()
- HashSet或HashMap则是根据hashCode()和equals()的返回值,如果都为true,说明这两元素或key相同,不存入重复元素
- 例一:hashCode()和equals()根据属性age,name判断相同,排序方法compareTo()按name降序。

见下图,因为name相同,所以TreeSet的size = 4。因为name,age相同所以HashSet的size = 5


- 例二:Person三个属性age,name,id。排序方法按id升序排序;hashCode()和equals()按age,name相同定义。集合TreeSet和HashSet添加id,age,name分别相同的对象。
public class Demo31_Person implements Comparable<Demo31_Person>{
String name;
int age;
int id;
public Demo31_Person(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
@Override
public int compareTo(Demo31_Person o) {
return this.id - o.id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Demo31_Person that = (Demo31_Person) o;
return age == that.age &&
Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "{name='" + name + '\'' +
", age=" + age + '\'' +
", id=" + id +
'}';
}
}
private static void demo31() {
Demo31_Person p1 = new Demo31_Person("aa", 12, 3);
Demo31_Person p2 = new Demo31_Person("aa", 30, 3);
Demo31_Person p3 = new Demo31_Person("ab", 18, 3);
Demo31_Person p4 = new Demo31_Person("ba", 2, 2);
Demo31_Person p5 = new Demo31_Person("bb", 2, 1);
Demo31_Person p6 = new Demo31_Person("bb", 2, 1);
Demo31_Person p7 = new Demo31_Person("bb", 2, 4);
Set<Demo31_Person> set1 = new TreeSet<>();//因为TreeSet,是直接按照排序规则存储
Set<Demo31_Person> set2 = new HashSet<>();//无序
List<Demo31_Person> list1 = new ArrayList<>();//按照添加顺序存储
Collections.addAll(set1, p1, p2, p3, p4, p5, p6, p7);
Collections.addAll(list1, p1, p2, p3, p4, p5, p6, p7);
Collections.addAll(set2, p1, p2, p3, p4, p5, p6, p7);
System.out.println("TreeSet: " + set1);
System.out.println("HashSet:" + set2);
System.out.println("ArrayList:" + list1);
Collections.sort(list1);//排序一下,参数只接收List接口下对象
System.out.println("排序后ArrayList:" + list1);
}
分析存储情况:HashSet无法存储age和name都相同的Person对象,TreeSet无法存储id相同的对象。并且TreeSet和ArrayList都以id升序排序
TreeSet: [{name='bb', age=2', id=1}, {name='ba', age=2', id=2}, {name='aa', age=12', id=3}, {name='bb', age=2', id=4}]
HashSet:[{name='bb', age=2', id=1}, {name='ab', age=18', id=3}, {name='ba', age=2', id=2}, {name='aa', age=12', id=3}, {name='aa', age=30', id=3}]
ArrayList:[{name='aa', age=12', id=3}, {name='aa', age=30', id=3}, {name='ab', age=18', id=3}, {name='ba', age=2', id=2}, {name='bb', age=2', id=1}, {name='bb', age=2', id=1}, {name='bb', age=2', id=4}]
排序后ArrayList:[{name='bb', age=2', id=1}, {name='bb', age=2', id=1}, {name='ba', age=2', id=2}, {name='aa', age=12', id=3}, {name='aa', age=30', id=3}, {name='ab', age=18', id=3}, {name='bb', age=2', id=4}]
总结速览表格

更多推荐



所有评论(0)