API篇(Java - 比较器(Comparable&Comparator))(doing)

目录

前言

一、方式一:实现Comparable接口(自然排序)

1. 定义与注意点

2. 实现步骤

3. 排序顺序:(默认都是从小到大排列的)

4. 源代码

5. 演示示例

5.1. 示例1

5.1.1. 分析 Comparable 函数式接口

5.1.2. Cat 实现 Comparable 接口

5.1.3. 手写排序算法, 这里用到的是冒泡排序

5.1.4. 测试类

5.2. 示例2

5.2.1. Pig 实现 Comparable 接口

5.2.2. 测试类

6. 总结

二、方式二:实现Comparator接口(定制排序)

1. 定义与注意点

2. 使用步骤

3. 常见API

3.1. reversed()

3.2. comparingInt

4. 演示示例

4.1. 示例1

4.1.1. 传统的Comparator示例

4.1.2. 等价的lambda表达式写法

4.1.3. 没有Lambda表达式的排序

4.1.4. Lambda表达式排序

4.1.5. 更多Lambda表达式的示例

三、Comparator & Comparable比较


前言

实现对象的排序,可以考虑两种方法:

  1. 自然排序(comparable)、定制排序(comparator)
  2. 内置比较器(Comparable)、外部比较器(Comparator)

一、方式一:实现Comparable接口(自然排序)

1. 定义与注意点

  • Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序。
  • 实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即通过 compareTo(Object obj) 方法的返回值来比较大小
    1. 如果当前对象this大于形参对象obj,则返回正整数,
    2. 如果当前对象this小于形参对象obj,则返回负整数,
    3. 如果当前对象this等于形参对象obj,则返回零。
  • 对于类 C 的每一个 e1 和 e2 来说,当且仅当 e1.compareTo(e2) == 0 与e1.equals(e2) 具有相同的 boolean 值时,类 C 的自然排序才叫做与 equals一致。建议(虽然不是必需的)最好使自然排序与 equals 一致。

2. 实现步骤

  1. 具体的类A实现Comparable类接口
  2. 重新Comparable接口中的compareTo(Object o)方法
  3. 在compareTo方法中指明比较的大小。

返回值为正代表大,为负代表小。一般来说this在前Object O在后是升序,this在后Object O在前是降序。

public class ComparableTest {
    @Test
    public void test() {
        People[] arr = new People[5];

        arr[0] = new People("Time",22);
        arr[1] = new People("Tom",13);
        arr[2] = new People("Jerry",13);
        arr[3] = new People("Lucy",19);
        arr[4] = new People("Rose",44);

        Arrays.sort(arr);
        for(People o:arr){
            System.out.println(o);
        }
        /*
        People{name='Jerry', age=13}
        People{name='Tom', age=13}
        People{name='Lucy', age=19}
        People{name='Time', age=22}
        People{name='Rose', age=44}
        */

    }
}
class People implements Comparable {

    public String name;
    public int age;

    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public People() {
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Object o) {
        if (o == this) {
            return 0;
        }
        
        if (o instanceof People) {
            People p = (People) o;
            //判断年纪、升序
            int value = Integer.compare(this.age, p.age);
            if(0 != value){
                return value;
            }

            //年龄相同的情况,按姓名排序
            return this.name.compareTo(p.name);


        }
        //抛出异常
        throw new RuntimeException("类型不一致");
    }

}

3. 排序顺序:(默认都是从小到大排列的)

  1. String:按照字符串中字符的Unicode值进行比较
  2. Character:按照字符的Unicode值来进行比较
  3. 数值类型对应的包装类以及BigInteger、BigDecimal:按照它们对应的数值大小进行比较
  4. Boolean:true对应的包装类实例大于 false 对应的包装类实例
  5. Date、Time等:后面的日期时间比前面的日期时间大

4. 源代码

Comparable 接口仅仅只包括一个函数,它的定义如下:

package java.lang;
import java.util.*;
 
public interface Comparable {
    public int compareTo(T o);
}

解释:

假设我们通过 x.compareTo(y) 来“比较x和y的大小”。

若返回“负数”,意味着“x比y小”;返回“零”,意味着“x等于y”;返回“正数”,意味着“x大于y”。

这里为了大家更好的理解Compareable这个接口, 我自己手写了一个Compareable接口。

5. 演示示例

5.1. 示例1

5.1.1. 分析 Comparable 函数式接口
package java.lang;
import java.util.*;
 
public interface Comparable {
    public int compareTo(T o);
}
5.1.2. Cat 实现 Comparable 接口
public class Cat implements Comparable<Cat> {
 
    private int height;
 
    private int weight;
 
    public Cat(int height, int weight){
        this.height = height;
        this.weight = weight;
    }
 
    public int compareTo(Cat cat){
        if(this.height>cat.height){
            return 1;
        } else if(this.height<cat.height){
            return -1;
        } else{
            return 0;
        }
    }
 
 
    @Override
    public String toString() {
        return "Cat{" +
                "height=" + height +
                ", weight=" + weight +
                '}';
    }
 
}
5.1.3. 手写排序算法, 这里用到的是冒泡排序
package com.company.strategy2;
 
public class Sorter {
 
    public static void sort(Comparable[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        for (int e = arr.length - 1; e > 0; e--) {
            for (int i = 0; i < e; i++) {
                if (arr[i].compareTo(arr[i + 1]) == 1) {
                    swap(arr, i, i + 1);
                }
            }
        }
    }
 
    public static void swap(Comparable[] arr, int i, int j) {
        Comparable temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}
5.1.4. 测试类
public class Main {
 
    public static void main(String[] args) {
        Cat[] cats = {new Cat(111,111), new Cat(555,555), new Cat(222,222), new Cat(444,444)};
        Sorter sorter = new Sorter();
        sorter.sort(cats);
        System.out.println(Arrays.toString(cats));
    }
}

实际使用中则不需要自己手写Comparable接口和sort排序算法

输出结果:

5.2. 示例2

5.2.1. Pig 实现 Comparable 接口
public class Pig implements java.lang.Comparable<Pig> {
 
    private Double weight;
 
    public Pig(Double weight){
        this.weight = weight;
    }
 
    @Override
    public int compareTo(Pig pig) {
        if (this.weight>pig.weight) {
            return 1;
        } else if (this.weight<pig.weight){
            return -1;
        } else {
            return 0;
        }
    }
 
    @Override
    public String toString() {
        return "Pig{" +
                "weight=" + weight +
                '}';
    }
}
5.2.2. 测试类
public class Test {
 
    public static void main(String[] args) {
        Pig[] pigs = new Pig[]{new Pig(123.5), new Pig(145.6), new Pig(111.6)};
        System.out.println(Arrays.toString(pigs));
 
        Arrays.sort(pigs);
 
        System.out.println(Arrays.toString(pigs));
    }
}

运行结果:

6. 总结

Comparable接口定义了compareTo方法,用于比较对象,Comparable接口是一个泛型接口。

在实现该接口时,泛型类型E被替换成一种具体的类型。

Java类库中的许多类实现了Comparable接口以定义对象的自然顺序。

Byte、Short、Integer、Long、Float、Double、Character、Biglnteger、BigDecimalxCalendar、String以及

Date类都实现了Comparable接口。

二、方式二:实现Comparator接口(定制排序)

1. 定义与注意点

  • 当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,
    或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序,强行对多个对象进行整体排序的比较
  • 重写compare(Object o1,Object o2)方法,比较o1和o2的大小:1. 如果方法返回正整数,则表示o1大于o2;2. 如果返回0,表示相等;3. 如果返回负整数,表示o1小于o2
  • 可以将 Comparator 传递给 sort 方法 (如 Collections.sort 或rrrays.sort),从而允许在排序顺序上实现精确控制。
  • 还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。

2. 使用步骤

  1. 创建Comparator的一个实例(可以采取匿名的创建方式)。
  2. 实现Comoarator接口的compare(类A o1, 类A o2)方法。

注意:此时o1相当于comparato(Object o)中的this,o2相当于Object o

  1. 传入sort方法中
Arrays.sort(arr, new Comparator<People>() {
    @Override
    public int compare(People o1, People o2) {
        if (o1 instanceof People && o2 instanceof People) {
            int value = o1.name.compareTo(o2.name);
            if (value != 0) {
                return value;
            } else {
                return Integer.compare(o1.age,o2.age);
            }
        }
        throw new RuntimeException("类型不匹配");
    }
});

3. 常见API

3.1. reversed()

default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
   }

顾名思义,可以知道这个方法是用来生成一个逆序器,比如我们开始需要得到一个正序的排序序列,然后又想得到一

个反转的排序序列,就可以使用该方法。

比如:

Integer [] values = new Integer[]{1,0,3,5,7,8,5,4,3,7,9,56,3,23};

Comparator<Integer> comparator = Comparator.comparingInt(x -> x);

Arrays.sort(values, comparator);

System.out.println(JSON.toJSONString(values));

Arrays.sort(values, comparator.reversed());

System.out.println(JSON.toJSONString(values));

结果如下:

[0,1,3,3,3,4,5,5,7,7,8,9,23,56]
[56,23,9,8,7,7,5,5,4,3,3,3,1,0]

3.2. comparingInt

用法:

static <T> Comparator<T> comparingInt(ToIntFunction <T> keyExtractor)

参数:此方法接受单个参数keyExtractor,该参数是用于提取整数排序键的函数。

返回值:此方法返回一个比较器,该比较器通过提取的键进行比较

异常:如果参数为null,则此方法将引发NullPointerException。

以下示例程序旨在说明compareingInt(java.util.function.ToIntFunction)方法:

示例1:

// Java program to demonstrate 
// Comparator.comparingInt(java.util.function.ToIntFunction)  method 
  
import java.util.Arrays; 
import java.util.Collections; 
import java.util.Comparator; 
import java.util.List; 
public class GFG { 
    public static void main(String[] args) 
    { 
  
        // create some user objects 
        User u1 = new User("Aaman", 25); 
        User u2 = new User("Joyita", 22); 
        User u3 = new User("Suvam", 28); 
        User u4 = new User("mahafuj", 25); 
  
        // before sort 
        List<User> list 
            = Arrays.asList(u2, u1, u4, u3); 
        System.out.println("Before Sort:"); 
        list.forEach(User 
                     -> System.out.println("User age "
                                           + User.getAge())); 
  
        Collections.sort(list, 
                         Comparator.comparingInt( 
                             User::getAge)); 
        System.out.println("\nAfterSort:"); 
        list.forEach(User 
                     -> System.out.println("User age "
                                           + User.getAge())); 
    } 
} 
class User implements Comparable<User> { 
    public String name; 
    public int age; 
  
    public User(String name, int age) 
    { 
        this.name = name; 
        this.age = age; 
    } 
  
    public int compareTo(User u1) 
    { 
        return name.compareTo(u1.name); 
    } 
  
    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 "User [name=" + name 
            + ", age=" + age + "]"; 
    } 
}

4. 演示示例

4.1. 示例1

在这个例子中,我们将会向你展示如何使用java 8的lambda表达式去写一个Comparator,去对List进行排序

4.1.1. 传统的Comparator示例
Comparator<Developer> byName = new Comparator<Developer>() {
        @Override
        public int compare(Developer o1, Developer o2) {
            return o1.getName().compareTo(o2.getName());
        }
    };
4.1.2. 等价的lambda表达式写法
Comparator<Developer> byName = 
        (Developer o1, Developer o2)->o1.getName().compareTo(o2.getName());
4.1.3. 没有Lambda表达式的排序

用Developer对象的年龄进行比较,通常情况下,你会使用Collections.sort,并传递一个匿名的Comparator

package com.zheng.java8;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class TestSorting {

    public static void main(String[] args) {

        List<Developer> listDevs = getDevelopers();

        System.out.println("Before Sort");
        for (Developer developer : listDevs) {
            System.out.println(developer);
        }
        
        //sort by age
        Collections.sort(listDevs, new Comparator<Developer>() {
            @Override
            public int compare(Developer o1, Developer o2) {
                return o1.getAge() - o2.getAge();
            }
        });
    
        System.out.println("After Sort");
        for (Developer developer : listDevs) {
            System.out.println(developer);
        }
        
    }

    private static List<Developer> getDevelopers() {

        List<Developer> result = new ArrayList<Developer>();

        result.add(new Developer("zheng", new BigDecimal("70000"), 33));
        result.add(new Developer("alvin", new BigDecimal("80000"), 20));
        result.add(new Developer("jason", new BigDecimal("100000"), 10));
        result.add(new Developer("iris", new BigDecimal("170000"), 55));
        
        return result;

    }   
}

输出:

Before Sort
Developer [name=zheng, salary=70000, age=33]
Developer [name=alvin, salary=80000, age=20]
Developer [name=jason, salary=100000, age=10]
Developer [name=iris, salary=170000, age=55]

After Sort
Developer [name=jason, salary=100000, age=10]
Developer [name=alvin, salary=80000, age=20]
Developer [name=zheng, salary=70000, age=33]
Developer [name=iris, salary=170000, age=55]

当排序要求改变是,我们仅仅只需要替换另一个新的匿名Comparator类

//sort by age
    Collections.sort(listDevs, new Comparator<Developer>() {
        @Override
        public int compare(Developer o1, Developer o2) {
            return o1.getAge() - o2.getAge();
        }
    });
    
    //sort by name	
    Collections.sort(listDevs, new Comparator<Developer>() {
        @Override
        public int compare(Developer o1, Developer o2) {
            return o1.getName().compareTo(o2.getName());
        }
    });
                
    //sort by salary
    Collections.sort(listDevs, new Comparator<Developer>() {
        @Override
        public int compare(Developer o1, Developer o2) {
            return o1.getSalary().compareTo(o2.getSalary());
        }
    });

上述代码可以正常运行,但是仅仅因为要修改一行代码,然后就创建一个类,有点奇怪。

4.1.4. Lambda表达式排序

在Java 8中,List支持Sort方法,不在需要去使用Collections.sort。

  //List.sort() since Java 8
    listDevs.sort(new Comparator<Developer>() {
        @Override
        public int compare(Developer o1, Developer o2) {
            return o2.getAge() - o1.getAge();
        }
    });

Lambda表达式示例

package com.zheng.java8;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class TestSorting {

    public static void main(String[] args) {

        List<Developer> listDevs = getDevelopers();
        
        System.out.println("Before Sort");
        for (Developer developer : listDevs) {
            System.out.println(developer);
        }
        
        System.out.println("After Sort");
        
        //lambda here!
        listDevs.sort((Developer o1, Developer o2)->o1.getAge()-o2.getAge());
    
        //java 8 only, lambda also, to print the List
        listDevs.forEach((developer)->System.out.println(developer));
    }

    private static List<Developer> getDevelopers() {

        List<Developer> result = new ArrayList<Developer>();

        result.add(new Developer("zheng", new BigDecimal("70000"), 33));
        result.add(new Developer("alvin", new BigDecimal("80000"), 20));
        result.add(new Developer("jason", new BigDecimal("100000"), 10));
        result.add(new Developer("iris", new BigDecimal("170000"), 55));
        
        return result;

    }
    
}

输出:

Before Sort
Developer [name=zheng, salary=70000, age=33]
Developer [name=alvin, salary=80000, age=20]
Developer [name=jason, salary=100000, age=10]
Developer [name=iris, salary=170000, age=55]

After Sort
Developer [name=jason, salary=100000, age=10]
Developer [name=alvin, salary=80000, age=20]
Developer [name=zheng, salary=70000, age=33]
Developer [name=iris, salary=170000, age=55]
4.1.5. 更多Lambda表达式的示例

通过年龄排序

//sort by age
    Collections.sort(listDevs, new Comparator<Developer>() {
        @Override
        public int compare(Developer o1, Developer o2) {
            return o1.getAge() - o2.getAge();
        }
    });
    
    //lambda
    listDevs.sort((Developer o1, Developer o2)->o1.getAge()-o2.getAge());
    
    //lambda, valid, parameter type is optional
    listDevs.sort((o1, o2)->o1.getAge()-o2.getAge());

通过名称排序

//sort by name
    Collections.sort(listDevs, new Comparator<Developer>() {
        @Override
        public int compare(Developer o1, Developer o2) {
            return o1.getName().compareTo(o2.getName());
        }
    });
        
    //lambda
    listDevs.sort((Developer o1, Developer o2)->o1.getName().compareTo(o2.getName()));		
    
    //lambda
    listDevs.sort((o1, o2)->o1.getName().compareTo(o2.getName()));

通过薪水排序

//sort by salary
    Collections.sort(listDevs, new Comparator<Developer>() {
        @Override
        public int compare(Developer o1, Developer o2) {
            return o1.getSalary().compareTo(o2.getSalary());
        }
    });				

    //lambda
    listDevs.sort((Developer o1, Developer o2)->o1.getSalary().compareTo(o2.getSalary()));
    
    //lambda
    listDevs.sort((o1, o2)->o1.getSalary().compareTo(o2.getSalary()));

lambda表达式,使用薪水给List排序

Comparator<Developer> salaryComparator = (o1, o2)->o1.getSalary().compareTo(o2.getSalary());
    listDevs.sort(salaryComparator);

输出

Developer [name=zheng, salary=70000, age=33]
Developer [name=alvin, salary=80000, age=20]
Developer [name=jason, salary=100000, age=10]
Developer [name=iris, salary=170000, age=55]

Lmabda表达式,使用薪水,倒排List

Comparator<Developer> salaryComparator = (o1, o2)->o1.getSalary().compareTo(o2.getSalary());
    listDevs.sort(salaryComparator.reversed());

输出

Developer [name=iris, salary=170000, age=55]
Developer [name=jason, salary=100000, age=10]
Developer [name=alvin, salary=80000, age=20]
Developer [name=zheng, salary=70000, age=33]

三、Comparator & Comparable比较

  1. Comparable 更像是自然排序
  2. Comparator 更像是定制排序

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: comparatorJava中的一个接口,用于比较两个对象的大小。它可以用于对集合中的元素进行排序,也可以用于自定义排序规则。实现comparator接口需要重写compare方法,该方法返回一个整数值,表示两个对象的大小关系。如果返回负数,则表示第一个对象小于第二个对象;如果返回正数,则表示第一个对象大于第二个对象;如果返回,则表示两个对象相等。comparator接口可以与Java中的排序算法一起使用,例如Collections.sort()方法。 ### 回答2: jmu-java-04面向对象进阶--02-接口-comparator讲述了Java中的接口以及比较器的使用。接口是一种约束,它规定了某个类必须要实现哪些方法,但不需要具体的实现方式。比较器则是一种接口,它规定了两个对象之间的排序方式。 在Java中,接口的定义方式为interface,其中的方法默认为public abstract形式。定义接口时,需要注意接口只能继承接口,并且可以有常量,但不能有成员变量。另外,接口中所有的方法都没有方法体,必须由实现它的类去具体实现。举例来说,如果我们定义一个接口Animal,可以定义一个方法move(),而实现这个接口的类必须实现move()方法,并且可以自由决定具体的实现方式,如Dog类可以实现为跑步,Bird类可以实现为飞行。 在讨论了接口的使用之后,jmu-java-04面向对象进阶--02-接口-comparator着重介绍了比较器的使用。比较器类似于一个工具箱,可以定义多种比较方式供其他类使用。比较器的核心类是Comparator,其定义的方法为compare(),用于比较两个对象并返回结果(0、1或-1)。比较器可以用于对对象进行排序或查找指定的对象。 在使用比较器时,需要实现Comparator接口,并覆盖compare()方法。比如,我们可以定义一个Person类,并在其中实现Comparator接口,然后在compare()方法中指定按照年龄从小到大排序。当我们使用Collections.sort()对Person列表进行排序时,就会按照我们定义的比较方式进行排序。 总的来说,jmu-java-04面向对象进阶--02-接口-comparator讲述了Java中的接口和比较器的使用,这是Java中优秀的编程方式之一,也是开发者必备的基本知识。掌握了接口和比较器的使用,我们就可以更好地实现面向对象编程,并对Java中的集合框架有更深刻的理解。 ### 回答3: ComparatorJava中一个非常重要的接口,它主要用于定义对象之间的比较规则。在Java中,比较规则是由比较器来实现的。比较器可以用于排序、查找和其他需要比较的场景。 Comparator接口有一个方法compare(Object o1, Object o2),用于比较两个对象的大小。如果o1大于o2,则该方法返回一个正整数;如果o1小于o2,则该方法返回一个负整数;如果o1等于o2,则该方法返回0。 我们可以使用Comparator接口来实现自定义的比较规则。比如,我们可以定义一个Student类,包含姓名和年龄两个属性,然后实现一个比较器,按照年龄从小到大的顺序对Student对象进行排序。 可以通过使用Collections.sort()方法对Student对象进行排序,提供一个实现Comparator接口的比较器作为参数进行排序。 实现一个比较器还可以实现多种排序方式。例如,按照姓名从小到大排序,实现如下: ``` public class NameComparator implements Comparator<Student> { public int compare(Student s1, Student s2) { return s1.getName().compareTo(s2.getName()); } } ``` 在使用时,我们可以将NameComparator对象作为参数传递给sort()方法,进行姓名排序。 Comparator接口的使用不仅仅局限于对象的比较排序,还可以用于其他需要比较的场景,比如查找、筛选等。例如,我们可以按照年龄筛选出年龄大于20岁的Student对象,并将它们存储在一个新的List中,实现如下: ``` List<Student> ageGreaterThan20 = students.stream() .filter(s -> s.getAge() > 20) .sorted(new AgeComparator()) .collect(Collectors.toList()); ``` 以上的代码使用了Java 8的新特性,使用流将年龄大于20岁的Student对象筛选出来,并按照年龄进行排序,最后存储在一个新的List中。 总之,Comparator是一个非常重要的接口,在Java中有着广泛的应用。掌握Comparator的使用可以帮助我们快速地实现对象比较、排序、筛选等操作,提高我们的编程效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值