Java8特性尝鲜之集合排序

只要提到Java8的lambda表达式,数组或者集合的排序都是一个非常棒的例子,这是因为自从Java1.2以来,只要一提到排序,Comparator这个接口总是挥之不去。有了Java8后,在很多排序中,Comparator都可以使用lambda表达式来替换了。

在接下来的例子中,我们会用到这个简单的Person类:

static class Person {
final String firstName;
final String lastName;

Person(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}

@Override
public String toString() {
    return "Person{" +
            "firstName='" + firstName + '\'' +
            ", lastName='" + lastName + '\'' +
            '}';
}

}
很明显,只要我们让Person类实现Comparable接口就可以让它支持排序了,不过我们现在考虑的是使用外部的Comparator进行排序的情况。来看下下面这个Person列表,其中名字是由某个在线随机名字生成器生成的:

List people =
Arrays.asList(
new Person(“Jane”, “Henderson”),
new Person(“Michael”, “White”),
new Person(“Henry”, “Brighton”),
new Person(“Hannah”, “Plowman”),
new Person(“William”, “Henderson”)
);
我们希望是先根据姓氏排序,然后根据名字排序。

Java 7中的排序

使用Comparator的一个经典的Java 7的例子是:

people.sort(new Comparator() {
@Override
public int compare(Person o1, Person o2) {
int result = o1.lastName.compareTo(o2.lastName);

if (result == 0)
  result = o1.firstName.compareTo(o2.firstName);

return result;

}
});
people.forEach(System.out::println);
上述代码的输出结果是:

Person{firstName=‘Henry’, lastName=‘Brighton’}
Person{firstName=‘Jane’, lastName=‘Henderson’}
Person{firstName=‘William’, lastName=‘Henderson’}
Person{firstName=‘Hannah’, lastName=‘Plowman’}
Person{firstName=‘Michael’, lastName=‘White’}
Java 8的排序
现在我们来把上面排序的代码改成Java8的风格:

Comparator c = (p, o) ->
p.lastName.compareTo(o.lastName);

c = c.thenComparing((p, o) ->
p.firstName.compareTo(o.firstName));

people.sort©;
people.forEach(System.out::println);
结果当然是一样的。上面的代码是什么意思?首先,我们将一个lambda表达式赋值给一个Person的Comparator的本地变量:

Comparator c = (p, o) ->
p.lastName.compareTo(.lastName);
不像Scala,c#或者Ceylon这些语言,他们可以从表达式中进行类型推导,本地变量的声明只需要用一个val关键字(或者类似的)就可以了,而Java是从变量声明到赋值的表达式进行推导的。

简单的话,可以这么说,类型推导是从左到右,而不是从右到左的。这样让Comparator显得有点笨重,因为Java不能当参数传递到sort方法时才做类型推导。

一旦我们把Comparator赋值给一个变量后,我们可以通过thenComparing方法很方便的把别的比较器增加到这个比较链中:

c = c.thenComparing((p, o) ->
p.firstName.compareTo(o.firstName));
最后,我们把这个Comparator传给列表的新的sort方法,它是List接口里面默认实现的一个方法:

default void sort(Comparator<? super E> c) {
Collections.sort(this, c);
}
上述局限性的一个解决方法

虽然Java类型推导的这个小缺陷令人有点沮丧,但是我们可以通过创建一个泛型的IdentityComparator来解决这个问题:

class Utils {
static Comparator compare() {
return (e1, e2) -> 0;
}
}
使用上面这个compare方法,我们就可以很流畅地完成一个比较器链的编写了:

people.sort(
Utils.compare()
.thenComparing((p, o) ->
p.lastName.compareTo(o.lastName))
.thenComparing((p, o) ->
p.firstName.compareTo(o.firstName))
);

people.forEach(System.out::println);
提取关键字
我们还可以进一步改进。因为我们一般只使用Comparator参数的POJO/DTO对象的同样一个值进行比较,我们用一个"提取关键值"的函数把它们传给这些新的API。下面是示例:

people.sort(Utils.compare()
.thenComparing(p -> p.lastName)
.thenComparing(p -> p.firstName));
people.forEach(System.out::println);
对于一个指定的Person对象,我们给接口提供一个提取的函数,比如,p.lastname。事实上,一旦我们使用了关键值提取器,我们可以省略上面的那个帮助方法,因为库里面也提供了一个comparing的方法来初始化整个调用链:

people.sort(
Comparator.comparing((Person p) -> p.lastName)
.thenComparing(p -> p.firstName));
people.forEach(System.out::println);
同样的,由于编译器无法进行类型推导,我们告诉它具体的类型,尽管在这个例子中sort方法已经提供了足够的信息了。想了解更多关于Java8的泛型推导的资料,请看下我们之前发的一些文章。

结论
跟Java5一样,升级最大的提升主要体现在JDK中。Java5给Comparator带来的是类型安全,而Java8则是让我们能更方便的使用它们。

原创文章转载请注明出处:Java8特性尝鲜之集合排序

英文原文链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值