Xem lại ví dụ ở phần 2.10: lấy ra tất cả các ngôn ngữ được sủ dụng ở Thuỵ Sĩ: Map countryLanguageSets = locales.collect( Collectors.toMap( l > l.getDisplayCountry(), l > Collections.singleton(l.getDisplayLanguage()), (a, b) > { Union of a and b Set r = new HashSet(a); r.addAll(b); return r; })); >Chúng ta phải tạo ra thiết lập đơn cho mỗi ngôn ngữ được tìm thấy. Sau đó ta kết hợp những ngôn ngữ đã có với ngôn ngữ mới được tìm thấy Đây là một cách dài dòng. Có cách khác trực tiếp hơn: dùng phương thức groupingBy Stream locales = Stream.of(Locale.getAvailableLocales()); Map countryToLocales = locales.collect( Collectors.groupingBy(Locale::getCountry)); List swissLocales = countryToLocales.get(CH); System.out.println(CH locales: + swissLocales); Kết quả:swissLocales: fr_CH, de_CH, it_CH Locale::getCountry là hàm phân loại của grouping. Chú ý: mỗi locale đều có 1 mã ngôn ngữ và mã quốc gia ví dụ: en_US là English ở Mĩ.
Trang 1LẬP TRÌNH JAVA
Giáo viên hướng dẫn: TS.Nguyễn Hồng Quang
1
Trang 2A Grouping and Partitioning
B Câu hỏi
Trang 3Xem lại ví dụ ở phần 2.10: lấy ra tất cả các ngôn ngữ được sủ dụng ở
Thuỵ Sĩ:
Map<String, Set<String>> countryLanguageSets = locales.collect(
Collectors.toMap(
l -> l.getDisplayCountry(),
l -> Collections.singleton(l.getDisplayLanguage()),
(a, b) -> { // Union of a and b
Set<String> r = new HashSet<>(a);
r.addAll(b);
return r; }));
->Chúng ta phải tạo ra thiết lập đơn cho mỗi ngôn ngữ được tìm thấy
Sau đó ta kết hợp những ngôn ngữ đã có với ngôn ngữ mới được tìm
thấy
3
Trang 4Þ Đây là một cách dài dòng Có cách khác trực tiếp hơn: dùng phương
thức groupingBy
Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
Map<String, List<Locale>> countryToLocales = locales.collect(
Collectors.groupingBy(Locale::getCountry));
List<Locale> swissLocales = countryToLocales.get("CH");
System.out.println("CH locales: " + swissLocales);
Kết quả:swissLocales: [fr_CH, de_CH, it_CH]
Locale::getCountry là hàm phân loại của grouping
Chú ý: mỗi locale đều có 1 mã ngôn ngữ và mã quốc gia ví dụ: en_US là English
ở Mĩ.
Trang 5Khi hàm phân loại là 1 hàm predicate, các yếu tố Stream được chia làm
2 danh sách, 1 là true, 2 là false Khi đó dùng partitioningBy hiệu quả
hơn groupingBy Ví dụ:
Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
Map<Boolean, List<Locale>> englishAndOtherLocales = locales.collect(
Collectors.partitioningBy(l -> l.getLanguage().equals("en")));
System.out.println("English locales: " + englishAndOtherLocales.get(true));
Kết quả: English locales: [en_US, en_SG, en_MT, en, en_PH, en_NZ,
en_ZA, en_AU, en_IE, en_CA, en_IN, en_GB]
5
Trang 6Phương thức groupingBy cho ra một map có giá trị là các list Nếu bạn
muốn dùng set thay vì list có thể sử dụng Collector.toSet Ví dụ:
Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
Map<String, Set<Locale>> countryToLocaleSet = locales.collect(
Collectors.groupingBy(Locale::getCountry, Collectors.toSet()));
System.out.println("countryToLocaleSet: " + countryToLocaleSet);
Trang 7Lớp Collectors còn cung cấp một số phương thức về việc nhóm như:
counting, summing, minBy, maxBy, mapping, reducing.
7
Trang 81 counting: đếm lại số phần tử được nhóm Ví dụ Đếm xem có bao
nhiêu locale trong quốc gia
Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
Map<String, Long> countryToLocalCounts =
locales.collect(Collectors.groupingBy(Locale::getCountry,
Collectors.counting()));
System.out.println(countryToLocalCounts);
Trang 92 summing(Int|Long|Double) :nhận vào các đối số, áp dụng các hàm
vào các phần tử, và đưa ra tổng của chúng, ví dụ tính tổng dân số trong
1 bang của thành phố:
Map<String, Integer> stateToCityPopulation =
cities.collect(Collectors.groupingBy(City::getState,
Collectors.summingInt(City::getPopulation)));
9
Trang 103.minBy, maxBy: lấy 1 bộ so sánh và sinh ra giá trị lớn nhất nhỏ nhất của
các phần tử trong stream Ví dụ:
Map<String, Optional<City>> stateToLargestCity =
cities.collect(Collectors.groupingBy(City::getState,
Collectors.maxBy(Comparator.comparing(City::getPopulation))));
Đưa ra thành phố có số dân lớn nhất trong bang
Trang 114.mapping: áp dụng một hàm cho kết quả nhóm, và nó yêu cầu một bộ
tập hợp khác khác để xử lý kết quả của nó Ví dụ:
Map<String, Optional<String>> stateToLongestCityName =
cities.collect(Collectors.groupingBy(City::getState,
Collectors.mapping(City::getName,
Collectors.maxBy(Comparator.comparing(String::length)))));
Ở đây, chúng ta nhóm thành phố theo bang Trong mỗi bang, chúng ta
sinh ra tên của các thành phố và lấy tên có độ dài lớn nhất
11
Trang 12Nếu việc hàm grouping và mapping có kiểu trả về là int, long, hay
double, bạn có thể tập hợp kết quả thành đối tượng summary statistics
Ví dụ:
Map<String, IntSummaryStatistics> stateToCityPopulationSummary =
cities.collect(
Collectors.groupingBy(City::getState,
Collectors.summarizingInt(City::getPopulation)));
từ đó bạn có thể tính tổng, đếm, giá trị trung bình, giá trị nhỏ nhất, lớn nhất của
các hàm giá trị từ đối tượng summary statistics của mỗi nhóm.
Trang 135 reducing: Các phương thức áp dụng một hoạt đông rút gon cho các
phần tử trong stream, 3 hình thức: reducing(binaryOperator),
reducing(identity, binaryOperator) và reducing(identity, mapper,
binaryOperator): Ở hình thức đầu tiên, phần tử định danh là null Ở
hình thức thứ ba, hàm mapping được áp dụng và giá trị của nó được rút
gọn
Đây là ví dụ lấy một xâu tên thành phố được tách ra bằng dấu phẩy ở
mỗi bang
Map<String, String> stateToCityNames = cities.collect(
groupingBy(City::getState, reducing("", City::getName, (s, t) -> s.length() == 0 ? t : s + ", " + t)));
13
Trang 14Collectors.reduce hiếm khi sử dụng đến Trong tình huống này, bạn có
thể đạt được cùng kết quả theo cách tự nhiên hơn:
Map<String, String> stateToCityNames = cities.collect(
groupingBy(City::getState, mapping(City::getName, joining(", "))));
Trang 15Kết luận: Các phương thức của Collectors ở trên có thể tạo ra các biểu
thức rất phức tạp Bạn nên chỉ sử dụng chúng với groupingBy và
partitioningBy đễ xử lý các giá trị được nhóm Nếu không thì đơn giản
gọi các phương thức chẳng hạn map, reduce, count, max, hay min trực
tiếp trên streams
15
Trang 16Câu 1: Phương thức minBy và maxBy trong lớp Collector được sử dụng
để làm gì?
A Lấy một bộ so sánh và sắp xếp theo giá trị lớn nhất, nhỏ nhất của
các phần tử trong stream
B Lấy một bộ so sánh và sắp xếp theo giá trị nhỏ nhất, lớn nhất của
các phần tử trong stream
C Lấy một bộ so sánh và đưa ra giá trị lớn nhất, nhỏ nhất của các phần
tử trong stream
D Lấy một bộ so sánh và đưa ra giá trị nhỏ nhất, lớn nhất của các phần
tử trong stream
Đáp án D
Trang 17Câu 2: Phương thức reducing trong lớp Collector có bao nhiêu hình
thức?
A 1
B 2
C 3
D 4
17
Đáp án C
Trang 18Câu 3: Vì sao nên sử dụng partitioningBy thay cho groupingBy khi hàm phân loại là 1 hàm predicate?
A Vì groupingBy không thể sử dụng khi hàm phân loại là hàm
predicate
B Vì partitioningBy thì dễ sử dụng hơn so với groupingBy khi hàm
phân loại là predicate
C Vì partitioningBy thì hiệu quả hơn groupingBy khi hàm phân loại là
predicate
D Cả B và C
Đáp án C