1. 程式人生 > >Java 8 辣麼大(lambda)表示式不慌之—–(五)示例-Collectors中的統計、分組、排序等

Java 8 辣麼大(lambda)表示式不慌之—–(五)示例-Collectors中的統計、分組、排序等

Java 8 辣麼大(lambda)表示式不慌之—–(五)示例-Collectors中的統計、分組、排序等


還是先定義好後面做示例要用的資料:

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 方法的定義:
partitioningBy 方法的定義
groupingBy方法的定義:
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());

道理是一樣的,寫法不一樣罷了。