Java Comparator接口学习笔记

Comparator是一个泛型函数式接口T表示待比较对象的类型

@FunctionalInterface
public interface Comparator<T> {
}

本文将主要介绍Comparator作为函数式接口的用法。

理解下面的内容需要以下知识:函数式接口、Lambda表达式、方法引用。


1. 抽象方法

唯一的(不是重写Object方法的)抽象方法: compare

int compare(T o1, T o2)

该方法根据o1, o2的大小关系返回:负数、0、正数

该方法的比较逻辑依赖于具体的Comparator对象,这样的Comparator对象通过Lambda表达式方法引用进行构造。

Comparator<String> comparator = Comparator.comparingInt(x -> x.length());  // 比较String类对象的长度,关于comparingInt下文会介绍,它返回一个Comparator对象
System.out.println(comparator.compare("Tom", "Jerry")); // -1

2. 常用静态/默认方法

① 静态方法comparing

comparing()有两种重载的形式:

// 源码1
// 功能:根据keyExtractor从输入中提取key,然后返回一个使用key类型默认compreTo()对key进行比较的Comparator对象
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
    Function<? super T, ? extends U> keyExtractor)
{
    Objects.requireNonNull(keyExtractor);
    return (Comparator<T> & Serializable)
        (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
  • 返回值:comparing返回一个Comparator对象,用于比较T类型对象

  • 参数:comparing的参数keyExtractor是一个Function类型,它接收T类型及T的超类型, 返回U类型及U的子类型。keyExtractor的逻辑用Lambda表达式或方法引用实现

    例如,输入String,提取其长度:

    Comparator<String> keyComparator = Comparator.comparing(String::length); // 从传入的String类型对象中提取(即extract) length (即key)
    keyComparator.compare("Tom", "Jerry");
  • 函数体:
    • 首先,确保keyExtractor不是null
    • 然后,返回一个Lambda表达式,该表达式对c1c2提取出来的key进行比较 (用的是c1c2类实现的compareTo()进行比较)
// 源码 2
// 功能: 根据keyExtractor从输入中提取key,然后返回一个根据keyComparator对key进行比较的Comparator对象
public static <T, U> Comparator<T> comparing(
    Function<? super T, ? extends U> keyExtractor, Comparator<? super U> keyComparator)
{
    Objects.requireNonNull(keyExtractor);
    Objects.requireNonNull(keyComparator);
    return (Comparator<T> & Serializable)
        (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
                                          keyExtractor.apply(c2));
}

与上面类似,只不过将key的比较逻辑由compareTo()替换成了自定义的比较逻辑keyComparator

实例:

package Lambda.ComparatorDemo;

import java.util.Arrays;
import java.util.Comparator;

class Person {
    private String fname;
    private String lname;

    public Person(String fname, String lname) {
        this.fname = fname;
        this.lname = lname;
    }

    public String getFname() {
        return fname;
    }

    public String getLname() {
        return lname;
    }

    @Override
    public String toString() {
        return fname + " " + lname;
    }
}

public class ComparingDemo {
    public static void main(String[] args) {
        Person p1 = new Person("Tom", "Kenn");
        Person p2 = new Person("Alice", "Zed");
        Person[] persons1 = {p1, p2};
        Person[] persons2 = persons1.clone();

        // 依据First Name排序(用的是String类的compareTo的比较逻辑)
        Arrays.sort(persons1, Comparator.comparing(Person::getFname));
        System.out.println("Person1 (Sorted by first name): ");
        for (Person p : persons1) {
            System.out.println(p);
        }

        System.out.println();

        // 依据First Name的长度排序
        Arrays.sort(persons2, Comparator.comparing(Person::getFname, Comparator.comparingInt(String::length)));
        System.out.println("Person2 (Sorted by length of first name):");
        for (Person p : persons2) {
            System.out.println(p);
        }
    }
}

② 静态方法comparingXXX()

  • comparingInt()
  • comparingDouble()
  • comparingLong()

comparing是一样的思路,只不过comparingXXX()comparing中的提取出来的key类型固定为了XXX

public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
    Objects.requireNonNull(keyExtractor);
    return (Comparator<T> & Serializable)
        (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1),                                       keyExtractor.applyAsInt(c2));
}

③ 默认方法thenComparing()

thenComparing是默认方法,需要对Comparator对象调用,它用于将多个Comparator结合起来实现“链式的比较”:先根据XXX比较,再根据XXX比较,...

// 源码
default Comparator<T> thenComparing(Comparator<? super T> other) {
    Objects.requireNonNull(other);
    return (Comparator<T> & Serializable) (c1, c2) -> {
        int res = compare(c1, c2);
        return (res != 0) ? res : other.compare(c1, c2);
    };
}
public class thenComparingDemo {
    public static void main(String[] args) {
        Person p1 = new Person("Alice", "Jessy");
        Person p2 = new Person("Alice", "Ann");
        Person p3 = new Person("Tom", "Smith");

        Person[] persons = {p1, p2, p3};

        // 先根据first name比较,再根据last name的长度比较
        Comparator<Person> chainedComparator = Comparator.comparing(Person::getFname)
                .thenComparing((x, y) -> (x.getLname().length() - y.getLname().length()));
        
         // 先根据first name排序,再根据last name的长度排序
        Arrays.sort(persons, chainedComparator);
        for (Person p : persons) {
            System.out.println(p);
        }
    }
}

/*
Alice Ann
Alice Jessy
Tom Smith

参考:

https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#thenComparing-java.util.Comparator

转载于:https://www.cnblogs.com/Yaigu/p/9533335.html

  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值