定製歸一化收集器(reducing)得到自定義結果集
阿新 • • 發佈:2018-12-22
reducing簡介
reducing 是一個收集器(操作),從字面意義上可以理解為“減少操作”:輸入多個元素,在一定的操作後,元素減少。
reducing 有多個過載方法,其中一個方法如下:
public static <T> Collector<T,?,Optional<T>> reducing(BinaryOperator<T> op)
以上方法,JDK對其的描述是:
Returns a Collector which performs a reduction of its input elements under a specified BinaryOperator. The result is described as an Optional. (返回一個收集器,該收集器在指定的二進位制操作符下執行其輸入元素的減少。結果被描述為可選的 <T>。)
reducing的應用
reducing 是一個非常有用的收集器,可以用在多層流、下游資料分組或分割槽等場合。
下面是一個例子:
給定一組 Person 物件,每個 Person 都有 city(所在城市)和 height(身高)屬性,編寫一個程式,統計出每個不同的城市最大、最小身高值。
import java.util.*; import java.util.function.BinaryOperator; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.reducing; /** * ReducingDemo * * @author Zebe */ public class ReducingDemo { /** * 執行入口 * * @param args 執行引數 */ public static void main(String[] args) { List<Person> personList = getPersonList(1000000); functionStyle(personList); normalStyle(personList); } /** * 函數語言程式設計風格(3行處理程式碼) * @param personList Person 列表 */ private static void functionStyle(List<Person> personList) { long start = System.currentTimeMillis(); // 建立一個比較器,取名為 byHeight (通過高度來比較) Comparator<Person> byHeight = Comparator.comparingInt(Person::getHeight); // 建立一個歸一收集器 Map<City, Optional<Person>> tallestByCity = personList.stream(). collect(groupingBy(Person::getCity, reducing(BinaryOperator.maxBy(byHeight)))); long usedTime = System.currentTimeMillis() - start; printResult("函數語言程式設計風格", personList.size(), usedTime, tallestByCity); } /** * 普通程式設計風格(20行處理程式碼) * @param personList Person 列表 */ private static void normalStyle(List<Person> personList) { long start = System.currentTimeMillis(); // 建立一個結果集 Map<City, Optional<Person>> tallestByCity = new HashMap<>(); // 第一步:找出所有的不同城市 Set<City> cityList = new HashSet<>(); for (Person person : personList) { if (!cityList.contains(person.getCity())) { cityList.add(person.getCity()); } } // 第二部,遍歷所有城市,遍歷所有人找出每個城市的最大身高 for (City city : cityList) { int maxHeight = 0; Person tempPerson = null; for (Person person : personList) { if (person.getCity().equals(city)) { if (person.getHeight() > maxHeight) { maxHeight = person.getHeight(); tempPerson = person; } } } tallestByCity.put(city, Optional.ofNullable(tempPerson)); } long usedTime = System.currentTimeMillis() - start; printResult("普通程式設計風格", personList.size(), usedTime, tallestByCity); } /** * 獲取Person列表 * @param numbers 要獲取的數量 * @return 返回指定數量的 Person 列表 */ private static List<Person> getPersonList(int numbers) { // 建立城市 final City cityChengDu = new City("成都"); final City cityNewYork = new City("紐約"); List<Person> people = new ArrayList<>(); // 建立指定數量的Person,並指定不同的城市和相對固定的身高值 for (int i = 0; i < numbers; i++) { if (i % 2 == 0) { // 成都最大身高185 people.add(new Person(cityChengDu, 185)); } else if (i % 3 == 0) { people.add(new Person(cityChengDu, 170)); } else if (i % 5 == 0) { // 成都最小身高160 people.add(new Person(cityChengDu, 160)); } else if (i % 7 == 0) { // 紐約最大身高200 people.add(new Person(cityNewYork, 200)); } else if (i % 9 == 0) { people.add(new Person(cityNewYork, 185)); } else if (i % 11 == 0) { // 紐約最小身高165 people.add(new Person(cityNewYork, 165)); } else { // 預設新增紐約最小身高165 people.add(new Person(cityNewYork, 165)); } } return people; } /** * 輸出結果 * @param styleName 風格名稱 * @param totalPerson 總人數 * @param usedTime 計算耗時 * @param tallestByCity 統計好最大身高的城市分組MAP */ private static void printResult(String styleName, long totalPerson, long usedTime, Map<City, Optional<Person>> tallestByCity) { System.out.println("\n" + styleName + ":計算 " + totalPerson + " 個人所在不同城市最大身高的結果如下:(耗時 " + usedTime + " ms)"); tallestByCity.forEach((city, person) -> { person.ifPresent(p -> System.out.println(city.getName() + " -> " + p.getHeight())); }); } }
Person類
/** * Person * * @author Zebe */ public class Person { /** * 所在城市 */ private City city; /** * 身高 */ private int height; /** * 構造器 * @param city 所在城市 * @param height 身高 */ public Person(City city, int height) { this.city = city; this.height = height; } public City getCity() { return city; } public void setCity(City city) { this.city = city; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } }
City類
/**
* City
*
* @author Zebe
*/
public class City {
/**
* 城市名
*/
private String name;
/**
* 構造器
* @param name 城市名
*/
public City(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
注:以上程式碼如果要計算最小值、平均值,將 maxBy 換成 minBy 就可以了。
輸出結果
程式輸出結果如下:
函數語言程式設計風格:計算 1000000 個人所在不同城市最大身高的結果如下:(耗時 149 ms)
成都 -> 185
紐約 -> 200
普通程式設計風格:計算 1000000 個人所在不同城市最大身高的結果如下:(耗時 82 ms)
成都 -> 185
紐約 -> 200
可以看出,函數語言程式設計的效率不一定會比普通程式設計效率更高,甚至相對要慢一點,但是,函數語言程式設計的好處在於:
- 把引數作為一個函式,而不是值,實現了只有在需要的時候才計算(惰性求值)。
- 使用 lambda 表示式能夠簡化程式表達的含義,使程式更簡潔明瞭。