Java 8 辣麼大(lambda)表示式不慌之—–(五)示例-Collectors中的統計、分組、排序等
Java 8 辣麼大(lambda)表示式不慌之—–(五)示例-Collectors中的統計、分組、排序等
- summarizingInt 按int型別統計
- maxBy取最大/minBy取最小
- averagingInt /averagingLong/averagingDouble取平均值
- counting計數
- partitioningBy分隔/groupingBy分組
- sorted 排序
還是先定義好後面做示例要用的資料:
List<User> listUser = new ArrayList<>(); listUser.add(new User("李白", 20, true)); listUser.add(new User("杜甫", 40, true)); listUser.add(new User("李清照", 18, false)); listUser.add(new User("李商隱", 23, true)); listUser.add(new User("杜牧", 39, true)); listUser.add(new User("蘇小妹", 16, false));
這個User就是一個普通的Bean物件,有name(姓名)、age(年齡)、gender(性別)三個屬性及對應的set/get方法。
summarizingInt 按int型別統計
IntSummaryStatistics summaryStatistics = listUser.stream().collect(Collectors.summarizingInt(User::getAge)); System.out.println("年齡平均值:" + summaryStatistics.getAverage()); // 年齡平均值:26.0 System.out.println("人數:" + summaryStatistics.getCount()); // 人數:6 System.out.println("年齡最大值:" + summaryStatistics.getMax()); // 年齡最大值:40 System.out.println("年齡最小值:" + summaryStatistics.getMin()); // 年齡最小值:16 System.out.println("年齡總和:" + summaryStatistics.getSum()); // 年齡總和:156
一個方法把統計相關的基本上都搞定了,美滋滋。這裡是按int統計,同樣他也有 summarizingLong、summarizingDouble
方法,跟這個類似。
嫌這個方法多餘的話,也有單個的統計方法。
maxBy取最大/minBy取最小
方法定義如下:
public static <T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator) { return reducing(BinaryOperator.maxBy(comparator)); }
引數傳的是一個Comparator,舉例說明:
// 根據指定條件取最大值: 取年紀最大的人
Optional<User> optional = listUser.stream().collect(Collectors.maxBy(Comparator.comparing((user) -> {
return user.getAge();
})));
if (optional.isPresent()) { // 判斷是否有值
User user = optional.get();
System.out.println("最大年紀的人是:" + user.getName()); // 輸出==》 最大年紀的人是:杜甫
}
取最小的與取最大的是一樣的道理:
// 根據指定條件取最小值: 取年紀最小的人
Optional<User> optional = listUser.stream().collect(Collectors.minBy(Comparator.comparing((user) -> {
return user.getAge();
})));
if (optional.isPresent()) { // 判斷是否有值
User user = optional.get();
System.out.println("最小年紀的人是:" + user.getName()); // 輸出==》 最小年紀的人是:蘇小妹
}
averagingInt /averagingLong/averagingDouble取平均值
方法定義:
public static <T> Collector<T, ?, Double>
averagingInt(ToIntFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new long[2],
(a, t) -> { a[0] += mapper.applyAsInt(t); a[1]++; },
(a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; },
a -> (a[1] == 0) ? 0.0d : (double) a[0] / a[1], CH_NOID);
}
傳一個ToIntFunction型別的引數,這裡說一下ToIntFunction。前面說過Function<T, R>的定義,T是入參的型別R是返回值的型別,與Function<T, R>相應的有IntFunction,他規定死入參型別是int,返回型別自定義;然後一個ToIntFunction他的入參型別是自定義,返回型別定死了是int。同理其他的Long型別的,Double型別的一樣。
所以我們此處的averagingInt這樣寫:
ToIntFunction<User> mapper = (user)->{
return user.getAge();
};
Double averageAge = listUser.stream().collect(Collectors.averagingInt(mapper ));
System.out.println("平均年齡是:" + averageAge); // 輸出--》 平均年齡是:26.0
或者
Double averageAge = listUser.stream().collect(Collectors.averagingInt(User::getAge));
System.out.println("平均年齡是:" + averageAge); // 輸出--》 平均年齡是:26.0
counting計數
counting方法很簡單,直接上例子:
int count = listUser.stream().collect(Collectors.counting()); // count其實就等於listUser.size();
此處單純數個數沒什麼用,相當於listUser.size(); 。但是可以結合其他方法使用,如結合filter使用求男性數量:
Long count = listUser.stream().filter(user -> user.getGender()).collect(Collectors.counting());
System.out.println("男性個數:" + count); // 輸出--》 男性個數:2
partitioningBy分隔/groupingBy分組
partitioningBy 方法的定義:
groupingBy方法的定義:
通過定義可以看到,partitioningBy的第一個引數是一個Predicate,而groupingBy的第一個引數是一個Function<T,R>,Predicate返回的是Boolean型別而Function<T,R>返回的是自己定義的型別。所以當分組的依據是Boolean型別的時候這兩個方法使用效果一樣,如:
// 將List中的人按性別分組
Predicate<User> predicate = (user) -> {
return user.getGender();
};
Map<Boolean, List<User>> partition = listUser.stream().collect(Collectors.partitioningBy(predicate));
Function<User, Boolean> classifier = (user) -> {
return user.getGender();
};
Map<Boolean, List<User>> groupby = listUser.stream().collect(Collectors.groupingBy(classifier));
上面2個方法得到的結果是一樣的,因為從上面的2個函式predicate和classifier的定義可以看到,2個函式的實現是一樣的。但是如果想要按其他型別的資料(如String)來進行分組的話 partitioningBy就要略遜一籌了,如按User的姓名來分組,partitioningBy就不好寫了,groupingBy可以稍微改一下就行:
Function<User, String> classifier = (user) -> {
return user.getName();
};
Map<String, List<User>> groupby = listUser.stream().collect(Collectors.groupingBy(classifier));
sorted 排序
這個方法不是Collectors的方法,是Stream接口裡面的一個方法。作用就是將Stream裡面的元素排序,但是有個前提是裡面的內容是Comparable的(或者說實現過Comparable介面的),否則會丟擲異常。
如上的listUser,如果直接寫:
List<User> sorted = listUser.stream().sorted().collect(Collectors.toList());
就會丟擲Exception in thread "main" java.lang.ClassCastException: User cannot be cast to java.lang.Comparable
異常。
所以要先讓User物件實現一下Comparable介面並重寫compareTo方法:
@Override
public int compareTo(User user) {
return this.getAge().compareTo(user.getAge());
}
這樣上面的sorted()方法就能按年齡來排序了。
或者User物件不實現一下Comparable介面,就直接寫:
List<User> sorted3 = listUser.stream()
.sorted((x, y) -> (x.getAge() < y.getAge()) ? -1 : ((x.getAge() > y.getAge()) ? 1 : 0))
.collect(Collectors.toList());
道理是一樣的,寫法不一樣罷了。