【Java 8 新特性】Java Comparator | 比较器

Java Comparator | 比较器

Java java.util.Comparator功能接口对对象的集合进行总排序。

为了对一个集合进行排序,我们将比较器(Comparator)实例传递给Stream.sortedCollections.sortList.sortArrays.sort方法。

比较器(Comparator)还可以控制SortedSet的顺序和SortedMap数据结构的键顺序。

比较器(Comparator)的功能方法是compare(T o1, T o2),当第一个参数小于、等于或大于第二个参数时,返回一个负整数、零或一个正整数。

比较器(Comparator)施加的排序应与equals方法一致。

只有当c.compare(e1, e2)==0对于集合中的每个e1e2具有与e1.equals(e2)相同的布尔值时,才称为一致,其中c是比较器(Comparator)的实例。

现在让我们详细讨论比较器(Comparator)示例。

比较器功能方法:compare(T o1, T o2)

compare是比较器功能接口的功能方法。从Java源代码中找到它的声明。

int compare(T o1, T o2) 

compare比较其两个参数的顺序。

当第一个参数小于、等于或大于第二个参数时,它返回负值、零值或正值。

找到定义比较方法的例子。

1. 使用 Lambda 表达式定义 compare

我们使用lambda表达式定义compare功能方法如下。

Comparator<Student> ageComp = (s1, s2) -> s1.getAge() - s2.getAge();
Comparator<Student> nameComp = (s1, s2) -> s1.getName().compareTo(s2.getName()); 

示例

CompareDemo.java

import java.util.Comparator;
import java.util.List;
public class CompareDemo {
  public static void main(String[] args) {
	List<Student> list = Student.getStudentList();
	
	System.out.println("--- Sort Students by age ---");
	
	Comparator<Student> ageComp = (s1, s2) -> s1.getAge() - s2.getAge();
	list.sort(ageComp);
	list.forEach(s -> System.out.println(s));
	
	System.out.println("--- Sort Students by name ---");
	
	Comparator<Student> nameComp = (s1, s2) -> s1.getName().compareTo(s2.getName());	
	list.sort(nameComp);
	list.forEach(s -> System.out.println(s));	
  }
} 

compare功能方法需要返回-ve0+ve的值。

s1.getAge()-s2.getAge()将返回-ve0+ve的值。

String.compareTo对两个字符串进行按字母顺序的比较,它返回-ve0+ve的值。

List.sort根据给定的比较器(Comparator)实例对这个列表进行排序。

现在找到一个在Student类中使用的示例。

Student.java

import java.util.Arrays;
import java.util.List;
public class Student implements Comparable<Student> {
  private String name;
  private int age;
  public Student(String name, int age) {
	this.name = name;
	this.age = age;
  }
  public String getName() {
	return name;
  }
  public int getAge() {
	return age;
  }
  @Override
  public int compareTo(Student s) {
	return name.compareTo(s.getName());
  }
  @Override  
  public String toString(){
	return name + "-" + age; 
  }
  public static List<Student> getStudentList(){
    Student s1 = new Student("Ram", 18);
    Student s2 = new Student("Shyam",22);
    Student s3 = new Student("Mohan",19);
    List<Student> list = Arrays.asList(s1,s2,s3);
    return list;
  }  
} 

输出

--- Sort Students by age ---
Ram-18
Mohan-19
Shyam-22
--- Sort Students by name ---
Mohan-19
Ram-18
Shyam-22 

2. 自定义 compare 实现比较器

CompareDemoImplement.java

import java.io.Serializable;
import java.util.Comparator;
import java.util.List;

class AgeComparator implements Comparator<Student>, Serializable {
  private static final long serialVersionUID = 1L;
  @Override
  public int compare(Student s1, Student s2) {
     return s1.getAge() - s2.getAge();
  }
}
class NameComparator implements Comparator<Student>, Serializable {
  private static final long serialVersionUID = 1L;
  @Override
  public int compare(Student s1, Student s2) {
     return s1.getName().compareTo(s2.getName());
  }
}
public class CompareDemoImplement {
  public static void main(String[] args) {
	List<Student> list = Student.getStudentList();
	
	System.out.println("--- Sort Students by age ---");
	
	AgeComparator ageComparator = new AgeComparator();
	list.sort(ageComparator);
	list.forEach(s -> System.out.println(s));
	
	System.out.println("--- Sort Students by name ---");
	
	NameComparator nameComparator = new NameComparator();
	list.sort(nameComparator);
	list.forEach(s -> System.out.println(s));
  }
} 

输出

--- Sort Students by age ---
Ram-18
Mohan-19
Shyam-22
--- Sort Students by name ---
Mohan-19
Ram-18
Shyam-22 

在实现比较器接口的同时实现Serializable是一个很好的做法,因为它们可能被用作可序列化数据结构(如TreeSetTreeMap)的排序方法。

比较器的使用

我们可以用Stream.sortedCollections.sortList.sortArrays.sort方法来使用我们的比较器。

Stream.sorted

Stream.sorted返回一个由这个流的元素组成的流,根据提供的比较器进行排序。

CompareDemoStreamSorted.java

import java.util.Comparator;
import java.util.List;
public class CompareDemoStreamSorted {
  public static void main(String[] args) {
	List<Student> list = Student.getStudentList();
	
	System.out.println("--- Sort Students by age ---");
	
	Comparator<Student> ageComp = (s1, s2) -> s1.getAge() - s2.getAge();
	list.stream().sorted(ageComp).forEach(s -> System.out.println(s));
	
	System.out.println("--- Sort Students by name ---");
	
	Comparator<Student> nameComp = (s1, s2) -> s1.getName().compareTo(s2.getName());	
	list.stream().sorted(nameComp).forEach(s -> System.out.println(s));	
  }
} 

使用的Student类为上面compare例子中的学生类。

Collections.sort

Collections.sort根据给定的比较器实例对指定的列表进行排序。

CompareDemoCollectionsSort.java

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class CompareDemoCollectionsSort {
  public static void main(String[] args) {
	List<Student> list = Student.getStudentList();
	
	System.out.println("--- Sort Students by age ---");
	
	Comparator<Student> ageComp = (s1, s2) -> s1.getAge() - s2.getAge();
	Collections.sort(list, ageComp);
	list.forEach(s -> System.out.println(s));
	
	System.out.println("--- Sort Students by name ---");
	
	Comparator<Student> nameComp = (s1, s2) -> s1.getName().compareTo(s2.getName());	
	Collections.sort(list, nameComp);
	list.forEach(s -> System.out.println(s));	
  }
} 

使用的Student类为上面compare例子中的学生类。

List.sort

List.sort根据给定的比较器实例对这个列表进行排序。

CompareDemoListSort.java

import java.util.Comparator;
import java.util.List;
public class CompareDemoListSort {
  public static void main(String[] args) {
	List<Student> list = Student.getStudentList();
	
	System.out.println("--- Sort Students by age ---");
	
	Comparator<Student> ageComp = (s1, s2) -> s1.getAge() - s2.getAge();
	list.sort(ageComp);
	list.forEach(s -> System.out.println(s));
	
	System.out.println("--- Sort Students by name ---");
	
	Comparator<Student> nameComp = (s1, s2) -> s1.getName().compareTo(s2.getName());	
	list.sort(nameComp);
	list.forEach(s -> System.out.println(s));	
  }
} 

使用的Student类为上面compare例子中的学生类。

Arrays.sort

Arrays.sort根据指定比较器产生的顺序对指定的对象数组进行排序。

CompareDemoArraysSort.java

import java.util.Arrays;
import java.util.Comparator;
public class CompareDemoArraysSort {
  public static void main(String[] args) {
        Student st1 = new Student("Ram", 18);
        Student st2 = new Student("Shyam",22);
        Student st3 = new Student("Mohan",19);
    
	Student[] array = {st1, st2, st3};
	
	System.out.println("--- Sort Students by age ---");
	
	Comparator<Student> ageComp = (s1, s2) -> s1.getAge() - s2.getAge();
	Arrays.sort(array, ageComp);
	for (Student s : array) {
	  System.out.println(s);
	}
	
	System.out.println("--- Sort Students by name ---");
	
	Comparator<Student> nameComp = (s1, s2) -> s1.getName().compareTo(s2.getName());	
	Arrays.sort(array, nameComp);
	for (Student s : array) {
	  System.out.println(s);
	}
  }
} 

使用的Student类为上面compare例子中的学生类。

比较器的方法

Java 8中,比较器接口引入了一些静态和默认的方法。

在这里,我们将结合实例讨论这些方法。

我们可以将比较器与Stream.sortedList.sortCollections.sortArrays.sort一起使用来对集合和Map进行排序。

1. reversed

reversed是Java比较器功能接口的默认方法。reversed返回一个比较器,该比较器强制执行反向排序。

声明如下。

default Comparator<T> reversed() 

要使用reversed方法,我们需要实例化我们的比较器并调用该方法。

reversed将返回新的比较器实例,该实例将强加该比较器的反向排序。

Comparator<Student> nameComparator = (s1, s2) -> s1.getName().compareTo(s2.getName());
Collections.sort(list, nameComparator.reversed()); 

示例,其中使用的Student类为上面compare例子中的学生类。

ComparatorReversedDemo.java

import java.util.Comparator;
import java.util.List;
import com.concretepage.Student;
public class ComparatorReversedDemo {
  public static void main(String[] args) {
	List<Student> list = Student.getStudentList();
	Comparator<Student> ageComparator = (s1, s2) -> s1.getAge() - s2.getAge();	
	list.stream().sorted(ageComparator.reversed()).forEach(s -> System.out.print(s.getAge() + " "));
	System.out.println("\n-----------");
	Comparator<Student> nameComparator = (s1, s2) -> s1.getName().compareTo(s2.getName());	
	list.stream().sorted(nameComparator.reversed()).forEach(s -> System.out.print(s.getName() + " "));
	System.out.println("\n-----------");	
	list.stream().sorted(Comparator.comparing(Student::getAge).reversed()).forEach(s -> System.out.print(s.getAge() + " "));
	System.out.println("\n-----------");
	list.stream().sorted(Comparator.comparing(Student::getName).reversed()).forEach(s -> System.out.print(s.getName() + " "));	
  }
} 

输出

22 19 18 
-----------
Shyam Ram Mohan 
-----------
22 19 18 
-----------
Shyam Ram Mohan 

2. reverseOrder

reverseOrder是一个静态方法,返回比较器,对对象集合进行反向自然排序。

对于自然排序,一个类需要实现比较器并定义compareTo方法。

一个对象集合根据自然排序中的compareTo进行排序。

Comparator.reverseOrder反转了自然排序。

它在内部调用Collections.reverseOrder()并返回比较器实例。

查找Comparator.reverseOrderJava源代码。

public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
     return Collections.reverseOrder();
} 

示例,其中使用的Student类为上面compare例子中的学生类。

ComparatorReverseOrderDemo.java

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ComparatorReverseOrderDemo {
  public static void main(String[] args) {
	List<Integer> numList = Arrays.asList(12, 10, 15, 8, 11);
	Collections.sort(numList, Comparator.reverseOrder());
	numList.forEach(n -> System.out.print(n + " "));
	System.out.println("\n-----------");
	
	List<String> strList = Arrays.asList("Varanasi", "Allahabad", "Kanpur", "Noida");
	Collections.sort(strList, Comparator.reverseOrder());
	strList.forEach(s -> System.out.print(s + " "));
	System.out.println("\n-----------");	
	
	List<Student> stdList = Student.getStudentList();
	Collections.sort(stdList, Comparator.reverseOrder());
	stdList.forEach(s -> System.out.print(s.getName() + " "));	
  }
} 

输出

15 12 11 10 8 
-----------
Varanasi Noida Kanpur Allahabad 
-----------
Shyam Ram Mohan 

3. naturalOrder

naturalOrder是比较器功能接口的静态方法。

Comparator.naturalOrder方法返回一个比较器,该比较器以自然顺序比较可比较的对象。

对于自然排序,一个类需要实现Comparable并定义compareTo方法。

一个对象的集合按照自然排序的compareTo方法进行排序。

IntegerStringDate这样的Java类实现了Comparable接口,并覆盖了其compareTo方法,它们以词汇表(lexicographic-order)排序。

Java源代码中找到naturalOrder方法声明。

static <T extends Comparable<? super T>> Comparator<T> naturalOrder() 

找到使用List.sort方法的Comparator.naturalOrder的例子。

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class ComparatorNaturalOrderDemo {
  public static void main(String[] args) {
	List<Integer> numList = Arrays.asList(12, 10, 15, 8, 11);
	numList.sort(Comparator.naturalOrder());
	numList.forEach(n -> System.out.print(n + " "));
	System.out.println("\n-----------");
	
	List<String> strList = Arrays.asList("Varanasi", "Allahabad", "Kanpur", "Noida");
	strList.sort(Comparator.naturalOrder());
	strList.forEach(s -> System.out.print(s + " "));
	System.out.println("\n-----------");	
	
	List<Student> stdList = Student.getStudentList();
	stdList.sort(Comparator.naturalOrder());
	stdList.forEach(s -> System.out.print(s.getName() + " "));
  }
} 

输出

8 10 11 12 15 
-----------
Allahabad Kanpur Noida Varanasi 
-----------
Mohan Ram Shyam 

4. nullsFirst

nullsFirst是比较器功能接口的静态方法。

Comparator.nullsFirst方法返回一个对null友好的比较器,它认为null小于非null

Java源代码中找到它的声明。

static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) 

找到由nullsFirst方法返回的比较器工作原理。

  1. 空元素被认为是小于非空元素的。
  2. 当两个元素都是空的时候,那么它们就被认为是相等的。
  3. 当两个元素都是非空的时候,指定的比较器决定了顺序。
  4. 如果指定的比较器是空的,那么返回的比较器认为所有非空的元素是相等的。

示例

NullsFirstDemo.java

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class NullsFirstDemo {
  public static void main(String[] args) {
	Student s1 = new Student("Ram", 18);
	Student s2 = new Student("Shyam", 22);
	Student s3 = new Student("Mohan", 17);

	System.out.println("-------Case1: One null----------");

	List<Student> list = Arrays.asList(s1, s2, null, s3);
	Collections.sort(list, Comparator.nullsFirst(Comparator.comparing(Student::getName)));
	list.forEach(s -> System.out.println(s));

	System.out.println("--------Case2: More than one null---------");

	list = Arrays.asList(s1, null, s2, null, s3);
	Collections.sort(list, Comparator.nullsFirst(Comparator.comparing(Student::getName)));
	list.forEach(s -> System.out.println(s));

	System.out.println("--------Case3: Reverse specified Comparator to nullsFirst---------");

	list = Arrays.asList(s1, null, s2, null, s3);
	Collections.sort(list, Comparator.nullsFirst(Comparator.comparing(Student::getName).reversed()));
	list.forEach(s -> System.out.println(s));

	System.out.println("--------Case4: Reverse Comparator returned by nullsFirst---------");

	list = Arrays.asList(s1, null, s2, null, s3);
	Collections.sort(list, Comparator.nullsFirst(Comparator.comparing(Student::getName)).reversed());
	list.forEach(s -> System.out.println(s));

	System.out.println("--------Case5: Specify natural order Comparator to nullsFirst---------");

	list = Arrays.asList(s1, null, s2, null, s3);
	Collections.sort(list, Comparator.nullsFirst(Comparator.naturalOrder()));
	list.forEach(s -> System.out.println(s));

	System.out.println("--------Case6: Specify null to nullsFirst---------");

	list = Arrays.asList(s1, null, s2, null, s3);
	Collections.sort(list, Comparator.nullsFirst(null));
	list.forEach(s -> System.out.println(s));
  }
}

输出

-------Case1: One null----------
null
Mohan-17
Ram-18
Shyam-22
--------Case2: More than one null---------
null
null
Mohan-17
Ram-18
Shyam-22
--------Case3: Reverse specified Comparator to nullsFirst---------
null
null
Shyam-22
Ram-18
Mohan-17
--------Case4: Reverse Comparator returned by nullsFirst---------
Shyam-22
Ram-18
Mohan-17
null
null
--------Case5: Specify natural order Comparator to nullsFirst---------
null
null
Mohan-17
Ram-18
Shyam-22
--------Case6: Specify null to nullsFirst---------
null
null
Ram-18
Shyam-22
Mohan-17 

5. nullsLast

nullsLast是比较器功能接口的静态方法。

Comparator.nullsLast方法返回一个对null友好的比较器,认为null大于非null

Java源代码中找到它的声明。

static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) 

找到由nullsLast方法返回的比较器工作原理。

  1. 空元素被认为是大于非空元素的。
  2. 当两个元素都是空的时候,那么它们就被认为是相等的。
  3. 当两个元素都是非空的时候,指定的比较器决定了顺序。
  4. 如果指定的比较器是空的,那么返回的比较器认为所有非空的元素是相等的。

示例

NullsLastDemo.java

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class NullsLastDemo {
  public static void main(String[] args) {
	Student s1 = new Student("Ram", 18);
	Student s2 = new Student("Shyam", 22);
	Student s3 = new Student("Mohan", 17);

	System.out.println("-------Case1: One null----------");

	List<Student> list = Arrays.asList(s1, s2, null, s3);
	Collections.sort(list, Comparator.nullsLast(Comparator.comparing(Student::getName)));
	list.forEach(s -> System.out.println(s));

	System.out.println("--------Case2: More than one null---------");

	list = Arrays.asList(s1, null, s2, null, s3);
	Collections.sort(list, Comparator.nullsLast(Comparator.comparing(Student::getName)));
	list.forEach(s -> System.out.println(s));

	System.out.println("--------Case3: Reverse specified Comparator to nullsLast---------");

	list = Arrays.asList(s1, null, s2, null, s3);
	Collections.sort(list, Comparator.nullsLast(Comparator.comparing(Student::getName).reversed()));
	list.forEach(s -> System.out.println(s));

	System.out.println("--------Case4: Reverse Comparator returned by nullsLast---------");

	list = Arrays.asList(s1, null, s2, null, s3);
	Collections.sort(list, Comparator.nullsLast(Comparator.comparing(Student::getName)).reversed());
	list.forEach(s -> System.out.println(s));

	System.out.println("--------Case5: Specify natural order Comparator to nullsLast---------");

	list = Arrays.asList(s1, null, s2, null, s3);
	Collections.sort(list, Comparator.nullsLast(Comparator.naturalOrder()));
	list.forEach(s -> System.out.println(s));

	System.out.println("--------Case6: Specify null to nullsLast---------");

	list = Arrays.asList(s1, null, s2, null, s3);
	Collections.sort(list, Comparator.nullsLast(null));
	list.forEach(s -> System.out.println(s));
  }
} 

输出

-------Case1: One null----------
Mohan-17
Ram-18
Shyam-22
null
--------Case2: More than one null---------
Mohan-17
Ram-18
Shyam-22
null
null
--------Case3: Reverse specified Comparator to nullsLast---------
Shyam-22
Ram-18
Mohan-17
null
null
--------Case4: Reverse Comparator returned by nullsLast---------
null
null
Shyam-22
Ram-18
Mohan-17
--------Case5: Specify natural order Comparator to nullsLast---------
Mohan-17
Ram-18
Shyam-22
null
null
--------Case6: Specify null to nullsLast---------
Ram-18
Shyam-22
Mohan-17
null
null 

6. comparing

comparing是比较器功能接口的静态方法。

Comparator.comparing接受一个函数,该函数从给定的类型中提取一个可比较的排序键,并返回一个通过该排序键进行比较的比较器。

Comparator.comparing有两种形式。

1.

static <T,U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T,? extends U> keyExtractor) 

我们需要传递一个函数,它将从一个类型T中提取一个可比较的排序键,并返回一个通过该排序键进行比较的比较器。

查找示例代码。

Comparator<Student> nameComparator = Comparator.comparing(Student::getName); 

2.

static <T,U> Comparator<T> comparing(Function<? super T,? extends U> keyExtractor, Comparator<? super U> keyComparator) 

我们需要传递一个函数和一个比较器。

该方法将从一个类型T中提取一个排序键,并返回一个比较器,使用指定的比较器对该排序键进行比较。

查找示例代码。

Comparator<Student> nameComparator = Comparator.comparing(Student::getName, (s1, s2) -> s2.compareTo(s1)); 

对于intlongdouble数据类型的排序键,比较器分别有comparingIntcomparingLongcomparingDouble方法。

这里我们将提供Comparator.comparing方法的例子。

找到一个将实现Comparable接口的类,并定义compareTo方法。

School.java

public class School implements Comparable<School> {
  private int code;
  private String sname;
  public School(int code, String sname) {
	this.code = code;
	this.sname = sname;
  }
  public int getCode() {
        return code;
  }
  public String getSname() {
        return sname;
  }
  @Override
  public int compareTo(School s) {
	return s.sname.compareTo(sname);
  }
} 

创建另一个类,我们将在其中创建School类型的类属性。

Student.java

import java.util.Arrays;
import java.util.List;
public class Student {
  private String name;
  private int age;
  private long homeDistance;  
  private double weight;
  private School school;
  public Student(String name, int age, long homeDistance, double weight, School school) {
	this.name = name;
	this.age = age;
	this.homeDistance = homeDistance;
	this.weight = weight;
	this.school = school;
  }
  public String getName() {
        return name;
  }
  public int getAge() {
        return age;
  }
  public long getHomeDistance() {
        return homeDistance;
  }
  public double getWeight() {
        return weight;
  }
  public School getSchool() {
        return school;
  }
  public static List<Student> getStudentList() {
	Student s1 = new Student("Ram", 18, 3455, 60.75, new School(101, "PQ College"));
	Student s2 = new Student("Shyam", 22, 3252, 65.80, new School(103, "RS College"));
	Student s3 = new Student("Mohan", 19, 1459, 65.20, new School(102, "AB College"));
	Student s4 = new Student("Mahesh", 20, 4450, 70.25, new School(104, "CD College"));
	List<Student> list = Arrays.asList(s1, s2, s3, s4);
	return list;
  }
} 

现在找到Comparator.comparing方法的例子。

ComparingDemo.java

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ComparingDemo {
  public static void main(String[] args) {
    List<Student> list = Student.getStudentList();
    
    Comparator<Student> schoolComparator1 = Comparator.comparing(Student::getSchool);
    Collections.sort(list, schoolComparator1);
    list.forEach(s->System.out.print(s.getName() + "-" + s.getSchool().getSname() + " | "));
    System.out.println("\n-------------------");    
    
    Comparator<Student> schoolComparator2 = 
    	Comparator.comparing(Student::getSchool, (sch1, sch2) -> sch1.getCode() - sch2.getCode());
    Collections.sort(list, schoolComparator2);
    list.forEach(s->System.out.print(s.getName() + "-" + s.getSchool().getCode() + " | "));
    System.out.println("\n-------------------");    
    
    Comparator<Student> nameComparator1 = Comparator.comparing(Student::getName); 
    Collections.sort(list, nameComparator1);
    list.forEach(s->System.out.print(s.getName() + " "));
    System.out.println("\n-------------------");

    Comparator<Student> nameComparator2 = Comparator.comparing(Student::getName, (s1, s2) -> s2.compareTo(s1));     
    Collections.sort(list, nameComparator2);
    list.forEach(s->System.out.print(s.getName() + " "));    
  }
} 

输出

Shyam-RS College | Ram-PQ College | Mahesh-CD College | Mohan-AB College | 
-------------------
Ram-101 | Mohan-102 | Shyam-103 | Mahesh-104 | 
-------------------
Mahesh Mohan Ram Shyam 
-------------------
Shyam Ram Mohan Mahesh 

7. comparingInt

comparingInt方法的声明。

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

它接受一个从类型T中提取一个int排序键的函数,并返回一个通过该排序键进行比较的比较器。

ComparingIntDemo.java

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ComparingIntDemo {
  public static void main(String[] args) {
    List<Student> list = Student.getStudentList();
    
    Collections.sort(list, Comparator.comparingInt(Student::getAge));
    list.forEach(s->System.out.print(s.getAge() + " "));    
  }
} 

输出

18 19 20 22 

8. comparingLong

comparingLong方法的声明。

static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) 

它接受一个从类型T中提取long排序键的函数,并返回一个通过该排序键进行比较的比较器。

ComparingLongDemo.java

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ComparingLongDemo {
  public static void main(String[] args) {
    List<Student> list = Student.getStudentList();
    
    Collections.sort(list, Comparator.comparingLong(Student::getHomeDistance));
    list.forEach(s->System.out.print(s.getHomeDistance() + " "));           
  }
} 

输出

1459 3252 3455 4450 

9. comparingDouble

comparingDouble方法的声明。

static <T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor) 

它接受一个从类型T中提取double排序键的函数,并返回一个通过该排序键进行比较的比较器。

ComparingDoubleDemo.java

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ComparingDoubleDemo {
  public static void main(String[] args) {
    List<Student> list = Student.getStudentList();
    
    Collections.sort(list, Comparator.comparingDouble(Student::getWeight));
    list.forEach(s->System.out.print(s.getWeight() + " "));           
  }
}

输出

60.75 65.2 65.8 70.25 

10. thenComparing

thenComparing是比较器功能接口的默认方法。

Comparator.thenComparing返回一个词表顺序(lexicographic-order)的比较器,该比较器被一个比较器实例调用,使用一组排序键对项目进行排序。

当这个比较器比较两个元素相等时,thenComparing方法决定了顺序。

我们可以多次使用Comparator.thenComparing

当我们想通过排序键组来确定元素的顺序时,要用到它。

对于intlongdouble数据类型的排序键,比较器分别有thenComparingIntthenComparingLongthenComparingDouble默认方法。

thenComparing有以下形式。

1.

default Comparator<T> thenComparing(Comparator<? super T> other) 

它返回一个词表顺序(lexicographic-order)比较器和另一个比较器。查找代码段。

Comparator<Student> compByStdName = Comparator.comparing(Student::getName);
Comparator<Student> schoolComparator1 = Comparator.comparing(Student::getAge) //sort by student age
	.thenComparing(compByStdName); //then sort by student name 

首先,比较器将按照学生年龄对学生集合进行排序,如果某些学生的年龄相同,那么将按照他们的名字进行排序。

2.

default <U extends Comparable<? super U>> Comparator<T> thenComparing(Function<? super T,? extends U> keyExtractor) 

它返回一个词表顺序(lexicographic-order)比较器,其中包含一个提取可比较排序键的函数。

Comparator<Student> schoolComparator2 = Comparator.comparing(Student::getSchool) //sort by school natural ordering i.e. city
	.thenComparing(Student::getAge) //then sort by student age
	.thenComparing(Student::getName); //then sort by student name 

首先,学生的集合将按照他们各自的学校的自然顺序进行排序,如果一些学生根据他们的学校排序是相同的,那么这些学生将按照他们各自的年龄进行排序,如果年龄也是相同的,那么他们将按照他们的名字进行排序。

3.

default <U> Comparator<T> thenComparing(Function<? super T,? extends U> keyExtractor, Comparator<? super U> keyComparator) 

它返回一个词表顺序(lexicographic-order)的比较器,该比较器带有一个函数,可以提取一个键与给定的比较器进行比较。

Comparator<Student> schoolComparator3 = Comparator.comparing(Student::getSchool) //sort by school natural ordering i.e. city
	.thenComparing(Student::getSchool, (school1, school2) -> school1.getSname().compareTo(school2.getSname())) //then sort by school name 
	.thenComparing(Student::getAge) //then sort by student age
	.thenComparing(Student::getName); //then sort by student name 

首先,学生的集合将按他们各自的学校的自然顺序进行排序(即在我们的演示中按学校所在城市),然后如果学生在同一学校所在城市,他们将按各自的学校名称进行排序,如果学生在同一学校名称,他们将按年龄进行排序,如果学生在同一年龄,他们将按名字进行排序。

完整的例子

School.java

public class School implements Comparable<School> {
  private String sname;
  private String city;  
  public School(String sname, String city) {
	this.sname = sname;
	this.city = city;
  }
  public String getSname() {
        return sname;
  }
  public String getCity() {
        return city;
  }
  @Override
  public int compareTo(School s) {
	return s.getCity().compareTo(city);
  }
} 

Student.java

import java.util.Arrays;
import java.util.List;
public class Student {
  private String name;
  private int age;
  private long homeDistance;
  private double weight;
  private School school;

  public Student(String name, int age, long homeDistance, double weight, School school) {
	this.name = name;
	this.age = age;
	this.homeDistance = homeDistance;
	this.weight = weight;
	this.school = school;
  }
  public String getName() {
	return name;
  }
  public int getAge() {
	return age;
  }
  public long getHomeDistance() {
	return homeDistance;
  }
  public double getWeight() {
	return weight;
  }
  public School getSchool() {
	return school;
  }
  public static List<Student> getStudentList() {
	Student s1 = new Student("Ram", 18, 3455, 60.75, new School("AB College", "Noida"));
	Student s2 = new Student("Shyam", 22, 3252, 65.80, new School("RS College", "Gurugram"));
	Student s3 = new Student("Mohan", 18, 1459, 65.20, new School("AB College", "Noida"));
	Student s4 = new Student("Mahesh", 22, 4450, 70.25, new School("RS College", "Gurugram"));
	List<Student> list = Arrays.asList(s1, s2, s3, s4);
	return list;
  }
} 

ThenComparingDemo.java

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ThenComparingDemo {
  public static void main(String[] args) {
    List<Student> list = Student.getStudentList();
    
    System.out.println("--------Example-1---------"); 
    
    Comparator<Student> compByStdName = Comparator.comparing(Student::getName);
    Comparator<Student> schoolComparator1 = Comparator.comparing(Student::getAge) //sort by student age
    	.thenComparing(compByStdName); //then sort by student name   
    
    Collections.sort(list, schoolComparator1);
    list.forEach(s->System.out.println(s.getName() + "-" + s.getAge()));
    
    System.out.println("--------Example-2---------");   
    
    Comparator<Student> schoolComparator2 = Comparator.comparing(Student::getSchool) //sort by school natural ordering i.e. city
    	.thenComparing(Student::getAge) //then sort by student age
    	.thenComparing(Student::getName); //then sort by student name   
    
    Collections.sort(list, schoolComparator2);
    list.forEach(s->System.out.println(s.getName() + "-" + s.getAge()+ "-" + s.getSchool().getCity()));
    
    System.out.println("--------Example-3---------");    
    
    Comparator<Student> schoolComparator3 = Comparator.comparing(Student::getSchool) //sort by school natural ordering i.e. city
    	.thenComparing(Student::getSchool, (school1, school2) -> school1.getSname().compareTo(school2.getSname())) //then sort by school name 
    	.thenComparing(Student::getAge) //then sort by student age
    	.thenComparing(Student::getName); //then sort by student name 
    
    Collections.sort(list, schoolComparator3);
    list.forEach(s->System.out.println(s.getName() + "-" + s.getAge()+ "-" + s.getSchool().getSname() + "-" + s.getSchool().getCity()));
  }
} 

输出

--------Example-1---------
Mohan-18
Ram-18
Mahesh-22
Shyam-22
--------Example-2---------
Mohan-18-Noida
Ram-18-Noida
Mahesh-22-Gurugram
Shyam-22-Gurugram
--------Example-3---------
Mohan-18-AB College-Noida
Ram-18-AB College-Noida
Mahesh-22-RS College-Gurugram
Shyam-22-RS College-Gurugram 

11. thenComparingInt

thenComparingInt方法声明。

default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) 

它返回一个词表顺序(lexicographic-order)比较器,其中包含一个提取int排序键的函数。找到例子。

ThenComparingIntDemo.java

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ThenComparingIntDemo {
  public static void main(String[] args) {
    List<Student> list = Student.getStudentList();
    
    Comparator<Student> comparator = Comparator.comparing(Student::getName, (s1, s2) -> s1.charAt(0) - s2.charAt(0))
    	.thenComparingInt(Student::getAge);
    
    Collections.sort(list, comparator);
    list.forEach(s->System.out.println(s.getName() + "-" + s.getAge()));    
  }
} 

输出

Mohan-18
Mahesh-22
Ram-18
Shyam-22 

12. thenComparingLong

thenComparingLong方法声明。

default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) 

它返回一个词表顺序(lexicographic-order)比较器,其中包含一个提取long排序键的函数。找到例子。

ThenComparingLongDemo.java

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ThenComparingLongDemo {
  public static void main(String[] args) {
    List<Student> list = Student.getStudentList();
    
    Comparator<Student> comparator = Comparator.comparing(Student::getName, (s1, s2) -> s1.charAt(0) - s2.charAt(0))
    	.thenComparingLong(Student::getHomeDistance);
    
    Collections.sort(list, comparator);
    list.forEach(s->System.out.println(s.getName() + "-" + s.getHomeDistance()));  
  }
} 

输出

Mohan-1459
Mahesh-4450
Ram-3455
Shyam-3252 

13. thenComparingDouble

thenComparingDouble方法声明。

default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) 

它返回一个词表顺序(lexicographic-order)比较器,其中包含一个提取double排序键的函数。找到例子。

ThenComparingDoubleDemo.java

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ThenComparingDoubleDemo {
  public static void main(String[] args) {
    List<Student> list = Student.getStudentList();
    
    Comparator<Student> comparator = Comparator.comparing(Student::getName, (s1, s2) -> s1.charAt(0) - s2.charAt(0))
    	.thenComparingDouble(Student::getWeight);
    
    Collections.sort(list, comparator);
    list.forEach(s->System.out.println(s.getName() + "-" + s.getWeight()));         
  }
} 

输出

Mohan-65.2
Mahesh-70.25
Ram-60.75
Shyam-65.8 

在 SortedSet 中使用比较器

Java Comparator可用于控制SortedSet数据结构的顺序。

SortedSet的实现类是TreeSetConcurrentSkipListSet

我们可以将比较器实例传递给TreeSetConcurrentSkipListSet类的构造函数以控制其顺序。

SortedSet提供了comparator()方法,用于返回用于对这个集合中的元素进行排序的比较器。

如果SortedSet对其元素使用自然排序,那么comparator()方法返回null

在这一页,我们将提供一些例子,说明如何在TreeSetConcurrentSkipListSet类中使用比较器。

1. 在TreeSet中使用比较器

TreeSet根据元素的自然排序,或者通过在集合创建时提供的比较器对元素进行排序,这取决于使用哪个构造函数。

我们可以通过使用以下构造函数传递比较器来实例化TreeSet类。

TreeSet(Comparator<? super E> comparator) 

它构建了一个新的、空的树集,根据指定的比较器进行排序。

当我们不传递比较器时,TreeSet会根据它的自然排序对元素进行排序。

对于自然排序,一个类需要实现Comparable接口并覆盖compareTo方法。

为了获得我们的TreeSet对象所使用的比较器,SortedSet提供了 comparator() 方法。

现在找到使用比较器与TreeSet类来控制其元素顺序的例子。使用本页面上面compare例子中的Student类。

TreeSetDemo.java

import java.util.Arrays;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo {
  public static void main(String[] args) {
	Student s1 = new Student("Shyam", 18);
	Student s2 = new Student("Mohan", 20);
	Student s3 = new Student("Ram", 22);
	
	System.out.println("---TreeSet Order With Comparator---");
	
	Comparator<Student> ageComparator = Comparator.comparing(Student::getAge);
	TreeSet<Student> myTreeSet = new TreeSet<>(ageComparator);
	myTreeSet.addAll(Arrays.asList(s1, s2, s3));
	myTreeSet.forEach(s -> System.out.println(s));	
	//System.out.println("Comparator: "+ myTreeSet.comparator());
	
	System.out.println("---TreeSet Natural Order (With Comparable)---");
	
	myTreeSet = new TreeSet<>();
	myTreeSet.addAll(Arrays.asList(s1, s2, s3));
	myTreeSet.forEach(s -> System.out.println(s));
  }
} 

输出

---TreeSet Order With Comparator---
Shyam-18
Mohan-20
Ram-22
---TreeSet Natural Order (With Comparable)---
Mohan-20
Ram-22
Shyam-18 

2. 在 ConcurrentSkipListSet 中使用比较器

ConcurrentSkipListSet根据元素的自然排序或在集合创建时提供的比较器来排序,这取决于使用哪种构造函数。

我们可以通过使用下面的构造函数传递比较器来实例化ConcurrentSkipListSet类。

ConcurrentSkipListSet(Comparator<? super E> comparator) 

它构造了一个新的、空的集合,根据指定的比较器对其元素进行排序。

当我们不传递比较器时,ConcurrentSkipListSet会根据其自然排序对元素进行排序。

对于自然排序,一个类需要实现Comparable接口并覆盖compareTo方法。

为了获得我们的ConcurrentSkipListSet对象所使用的比较器,SortedSet提供了comparator()方法。

现在找到使用比较器与ConcurrentSkipListSet类来控制其元素顺序的例子。

ConcurrentSkipListSetDemo.java

import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.ConcurrentSkipListSet;
public class ConcurrentSkipListSetDemo {
  public static void main(String[] args) {
	Student s1 = new Student("Shyam", 18);
	Student s2 = new Student("Mohan", 20);
	Student s3 = new Student("Ram", 22);
	
	System.out.println("---ConcurrentSkipListSet Order With Comparator---");
	
	Comparator<Student> ageComparator = Comparator.comparing(Student::getAge);
	ConcurrentSkipListSet<Student> myConcurrentSkipList = new ConcurrentSkipListSet<>(ageComparator);
	myConcurrentSkipList.addAll(Arrays.asList(s1, s2, s3));
	myConcurrentSkipList.forEach(s -> System.out.println(s));	
	//System.out.println("Comparator: "+ myConcurrentSkipList.comparator());
	
	System.out.println("---ConcurrentSkipListSet Natural Order (With Comparable)---");
	
	myConcurrentSkipList = new ConcurrentSkipListSet<>();
	myConcurrentSkipList.addAll(Arrays.asList(s1, s2, s3));
	myConcurrentSkipList.forEach(s -> System.out.println(s));
  }
}

输出

---ConcurrentSkipListSet Order With Comparator---
Shyam-18
Mohan-20
Ram-22
---ConcurrentSkipListSet Natural Order (With Comparable)---
Mohan-20
Ram-22
Shyam-18 

在 SortedMap 使用比较器

Java比较器可用于控制SortedMap数据结构的键的顺序。

SortedMap的实现类是TreeMapConcurrentSkipListMap

我们可以将比较器实例传递给TreeMapConcurrentSkipListMap类的构造函数以控制其键的顺序。

SortedMap提供了comparator()方法,用于返回用于对该Map中的键进行排序的比较器。

如果SortedMap使用key元素的自然排序,那么comparator()方法返回null

在这一页,我们将提供一些例子,说明如何在TreeMapConcurrentSkipListMap类中使用比较器。

1. 在 TreeMap 中使用比较器

TreeMap根据其键的自然排序或在Map创建时提供的比较器进行排序,这取决于使用哪个构造函数。

我们可以通过使用以下构造函数传递比较器来实例化TreeMap类。

TreeMap(Comparator<? super K> comparator) 

它构造一个新的、空的TreeMap,根据给定的比较器进行排序。

当我们不传递比较器时,TreeMap会按其自然顺序对键进行排序。

对于自然排序,元素类需要实现Comparable接口并重写compareTo方法。

为了获得我们的TreeMap对象所使用的比较器,SortedMap提供了comparator()方法。

现在找到使用比较器与TreeMap类来控制其键的顺序的例子。使用本页面上面compare例子中的Student类。

TreeMapDemo.java

import java.util.Comparator;
import java.util.TreeMap;
public class TreeMapDemo {
  public static void main(String[] args) {
	Student s1 = new Student("Shyam", 18);
	Student s2 = new Student("Mohan", 20);
	Student s3 = new Student("Ram", 22);
	
	System.out.println("---TreeMap Order With Comparator---");
	
	Comparator<Student> ageComparator = Comparator.comparing(Student::getAge);
	TreeMap<Student, String> myTreeMap = new TreeMap<>(ageComparator);
	myTreeMap.put(s1, "Varanasi");
	myTreeMap.put(s2, "Mathura");
	myTreeMap.put(s3, "Kashi");	
	myTreeMap.forEach((k, v) -> System.out.println(k + " - " + v));	
	//System.out.println("Comparator: "+ myTreeMap.comparator());
	
	System.out.println("---TreeMap Natural Order (With Comparable)---");
	
	myTreeMap = new TreeMap<>();
	myTreeMap.put(s1, "Varanasi");
	myTreeMap.put(s2, "Mathura");
	myTreeMap.put(s3, "Kashi");	
	myTreeMap.forEach((k, v) -> System.out.println(k + " - " + v));	
  }
} 

输出

---TreeMap Order With Comparator---
Shyam-18 - Varanasi
Mohan-20 - Mathura
Ram-22 - Kashi
---TreeMap Natural Order (With Comparable)---
Mohan-20 - Mathura
Ram-22 - Kashi
Shyam-18 - Varanasi 

2. 在 ConcurrentSkipListMap 中使用比较器

ConcurrentSkipListMap根据其键的自然排序,或者根据Map创建时提供的比较器进行排序,这取决于使用哪个构造函数。

我们可以通过使用下面的构造函数传递比较器来实例化ConcurrentSkipListMap类。

ConcurrentSkipListMap(Comparator<? super K> comparator) 

它构造一个新的、空的Map,根据给定的比较器排序。

当我们不传递比较器时,ConcurrentSkipListMap会根据其自然排序对键进行排序。

对于自然排序,元素类需要实现Comparable接口并重写compareTo方法。

为了获得我们的ConcurrentSkipListMap对象所使用的比较器,SortedMap提供了comparator()方法。

现在找到使用比较器与ConcurrentSkipListMap类来控制其键的顺序的例子。

ConcurrentSkipListMapDemo.java

import java.util.Comparator;
import java.util.concurrent.ConcurrentSkipListMap;
public class ConcurrentSkipListMapDemo {
  public static void main(String[] args) {
	Student s1 = new Student("Shyam", 18);
	Student s2 = new Student("Mohan", 20);
	Student s3 = new Student("Ram", 22);
	
	System.out.println("---ConcurrentSkipListMap Order With Comparator---");
	
	Comparator<Student> ageComparator = Comparator.comparing(Student::getAge);
	ConcurrentSkipListMap<Student, String> myConcurrentSkipListMap = new ConcurrentSkipListMap<>(ageComparator);
	myConcurrentSkipListMap.put(s1, "Varanasi");
	myConcurrentSkipListMap.put(s2, "Mathura");
	myConcurrentSkipListMap.put(s3, "Kashi");	
	myConcurrentSkipListMap.forEach((k, v) -> System.out.println(k + " - " + v));	
	//System.out.println("Comparator: "+ myConcurrentSkipListMap.comparator());
	
	System.out.println("---ConcurrentSkipListMap Natural Order (With Comparable)---");
	
	myConcurrentSkipListMap = new ConcurrentSkipListMap<>();
	myConcurrentSkipListMap.put(s1, "Varanasi");
	myConcurrentSkipListMap.put(s2, "Mathura");
	myConcurrentSkipListMap.put(s3, "Kashi");	
	myConcurrentSkipListMap.forEach((k, v) -> System.out.println(k + " - " + v));	
  }
} 

输出

---ConcurrentSkipListMap Order With Comparator---
Shyam-18 - Varanasi
Mohan-20 - Mathura
Ram-22 - Kashi
---ConcurrentSkipListMap Natural Order (With Comparable)---
Mohan-20 - Mathura
Ram-22 - Kashi
Shyam-18 - Varanasi 

参考文献

【1】Interface Comparator
【2】Interface SortedSet
【3】Interface SortedMap
【4】Java Comparator

上一篇:Java 中 Comparable 和 Comparator复习


下一篇:Java中的sort方法总结