❤ 概括典例:分别使用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}]

总结速览表格

在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐