我们先来看两个题
计算一个字符串中小写字母个数
计算个数我们知道可以用count方法,然后剩下就是找到字符串中的小写字母,使用String对象的chars方法可以拿到字符列表,然后过滤小写字母
public static int countLowercaseLetters(String string) { return (int) string.chars() .filter(Character::isLowerCase) .count(); }
在一个字符串列表中,找出包含最多小写字母的字符串,对于空列表,返回Optional<String> 对象
这里我们接收的是一个字符串列表,要输出的是一个Optional,这里还要找的是最多,看到最多我们可以想到用max方法,max接收的是一个比较器,可以用来比较大小
public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Hello world","hello","welcome"); mostLowercaseString(strings).ifPresent(System.out::println);//Hello world } public static int countLowercaseLetters(String string) { return (int)string.chars().filter(Character::isLowerCase).count(); } public static Optional<String> mostLowercaseString(List<String> strings) { return strings.stream().max(Comparator.comparingInt(Main::countLowercaseLetters)); } }
这里我们主要来分析第二个问题,重点看这里
strings.stream().max(Comparator.comparingInt(Main::countLowercaseLetters));
max函数接收的是一个Compartor比较器,返回一个Optional
Optional<T> max(Comparator<? super T> comparator);
Comparator我们在Java8之前就已经很熟悉了,以前我们会这样来生成一个Compartor
new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return countLowercaseLetters(s1) - countLowercaseLetters(s2);
}
}
在Java8中,Compartor已经变成了一个函数式接口,并且封装了很多常用的默认方法
@FunctionalInterface
public interface Comparator<T>
所以刚才的代码我们可以写成下面这种Labmda表达式的形式
(s1,s2)->countLowercaseLetters(s1)-countLowercaseLetters(s2)
这种最常用的比较两个数字大小的Compartor内部也封装了默认方法,叫做comparingInt
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));
}
可以看到其内部实现
(c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
就是我们上面(s1,s2)->countLowercaseLetters(s1)-countLowercaseLetters(s2)
这种Lambda表达式,只不过它封装到comparingInt方法中,对外调用更加方便
它内部Lambda表达式右边具体的实现是这样写的
Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
这里有个keyExtractor
,这个ToIntFunction<? super T>
作为方法参数传递进来的,是一个Lambda表达式
@FunctionalInterface
public interface ToIntFunction<T> {
/**
* Applies this function to the given argument.
*
* @param value the function argument
* @return the function result
*/
int applyAsInt(T value);
}
这个函数式接口接收一个值,返回一个数字,也就是说comparingInt方法将这种行为作为参数传递进来,方法内部只进行Integer.compare
比较,具体比较的数字是多少,是交给传进来的Lambda表达式来描述这个行为的,所以我们用comparingInt方法来实现就可以这样写:
strings.stream().max(Comparator.comparingInt(item -> countLowercaseLetters(item)));
item -> countLowercaseLetters(item)
这个Lambda表达式正好符合方法引用的规则,所以可以简写成
strings.stream().max(Comparator.comparingInt(Main::countLowercaseLetters));
总结
Lambda表达式在流的使用中是很常见的一种操作,其中比较器也是很常用的一种操作,熟练使用其内部封装好的默认方法有助于我们提高效率。