Java8 - 比较器

2020/02/12

2020_0322更新

这次更新主要是为了理清楚ComparableComparator的区别。

先来说Comparable

首先,它是一个接口,并且只有一个实例方法:

public int compareTo(T o);

一般的用法是将某个类实现Comparable接口,并重写compareTo()方法,比较指定的某个实例变量。

举个例子:

@Getter
public class Product implements Comparable<Product>{

    private String name;
    private int    price;

    public Product(String name, int price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public int compareTo(Product o) {
        return this.getPrice() - o.getPrice();
    }

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

@Test
void comparableTest(){
    Product a = new Product("A", 50);
    Product b = new Product("B", 10);
    Product c = new Product("C", 15);

    ArrayList<Product> products = new ArrayList<>();
    products.add(a);
    products.add(b);
    products.add(c);
    Collections.sort(products);
    System.out.println(products);
    // [Product{name='B', price=10}, Product{name='C', price=15}, Product{name='A', price=50}]
}

如果我想比较的对象没有实现Comparable接口,怎么办?

这个时候就可以用到Comparator

它也是个接口,而且还是函数式接口,并且有更为丰富的方法,如:reversed()thenComparing()等。

举个例子:

@Getter
public class Order {

    private String userName;
    private double total;

    public Order(String userName, double total) {
        this.userName = userName;
        this.total = total;
    }

    @Override
    public String toString() {
        return "Order{" +
                "userName='" + userName + '\'' +
                ", total=" + total +
                '}';
    }
}

public class OrderComparator implements Comparator<Order> {
    @Override
    public int compare(Order o1, Order o2) {
        return (int) (o1.getTotal() - o2.getTotal());
    }
}

@Test
void comparatorTest1(){
    Order a = new Order("a", 200.00d);
    Order b = new Order("b", 200.00d);
    Order c = new Order("c", 188.00d);
    Order d = new Order("d", 300.00d);

    ArrayList<Order> orders = new ArrayList<>();
    orders.add(a);
    orders.add(b);
    orders.add(c);
    orders.add(d);

    Collections.sort(orders, new OrderComparator());
    System.out.println(orders);
    // [Order{userName='c', total=188.0}, Order{userName='a', total=200.0}, Order{userName='b', total=200.0}, Order{userName='d', total=300.0}]
}

我们也可以使用ArrayList中的sort方法:

orders.sort(new OrderComparator());
System.out.println(orders);

我们还可以使用匿名类:

orders.sort(new Comparator<Order>() {
    @Override
    public int compare(Order o1, Order o2) {
        return (int) (o1.getTotal() - o2.getTotal());
    }
});
System.out.println(orders);

继续,Lambda表达式:

orders.sort((o1, o2) -> (int) (o1.getTotal() - o2.getTotal()));
System.out.println(orders);

再来,comparing()方法引用

orders.sort(Comparator.comparing(Order::getTotal));

最后再来玩下reversed()thenComparing()

orders.sort(Comparator.comparing(Order::getTotal)
    .reversed()
    .thenComparing(Order::getUserName));
System.out.println(orders);
// [Order{userName='d', total=300.0}, Order{userName='a', total=200.0}, Order{userName='b', total=200.0}, Order{userName='c', total=188.0}]

准备数据

List<Apple> inventory = Arrays.asList(
        new Apple(80,"green"),
        new Apple(155, "green"),
        new Apple(120, "red"),
        new Apple(80, "red"));

正序排序

@Test
public void complexComparatorLambdaTest(){
    List<Apple> sortedByWeight = inventory.stream()
            .sorted(Comparator.comparing(Apple::getWeight))
            .collect(Collectors.toList());
    System.out.println(sortedByWeight);

    // [Apple{color='green', weight=80}, Apple{color='red', weight=80}, Apple{color='red', weight=120}, Apple{color='green', weight=155}]
}

倒序排序

@Test
public void complexComparatorLambdaTest(){
     List<Apple> sortedByWeightReversely = inventory.stream()
                .sorted(Comparator.comparing(Apple::getWeight).reversed())
                .collect(Collectors.toList());
    System.out.println(sortedByWeightReversely);

    // [Apple{color='green', weight=155}, Apple{color='red', weight=120}, Apple{color='green', weight=80}, Apple{color='red', weight=80}]
}

比较器链

比如: 先根据重量正序,然后根据颜色倒序

@Test
public void complexComparatorLambdaTest(){
    List<Apple> sortedByWeightAndColor = inventory.stream()
                .sorted(Comparator.comparing(Apple::getWeight)
                                  .thenComparing(Comparator.comparing(Apple::getColor)
                                  .reversed()))
                .collect(Collectors.toList());
    System.out.println(sortedByWeightAndColor);

    // [Apple{color='red', weight=80}, Apple{color='green', weight=80}, Apple{color='red', weight=120}, Apple{color='green', weight=155}]
}

Reference

  • 《Java8实战》 - 3.8.1 比较器复合


一位喜欢提问、尝试的程序员

(转载本站文章请注明作者和出处 姚屹晨-yaoyichen

Post Directory