Java 8 引入的 Stream API 對於集合的操作提供了一種新的方式,包括篩選,分組和映射等。Stream API 中的 groupby 方法可以將一個 Stream 中的元素分組為一個 Map ,以實現更高效的聚合和操作。本文將從多個方面對 Stream 中的 groupby 進行詳細講解。
一、基本用法
Stream 中的 groupby 方法可以在一個 Stream 中根據給定的分類器對元素進行分組,以生成一個 Map 。示例代碼如下:
List people = Arrays.asList( new Person("John", 23), new Person("Alice", 25), new Person("John", 28) ); Map<String, List> peopleByNames = people.stream() .collect(Collectors.groupingBy(Person::getName)); System.out.println(peopleByNames); // 輸出結果:{John=[Person{name='John', age=23}, Person{name='John', age=28}], Alice=[Person{name='Alice', age=25}]}
上面的代碼中,我們先定義了一個 Person 類,並創建了一個包含三個 Person 實例的 List 對象。我們接着通過 Stream API 中的 groupingBy 方法,對這個 List 對象進行分組,將相同名字的 Person 實例放在同一個 List 對象中,生成一個 Map 對象。在上面的示例中可以看出,同名的 John 實例被分為了同一組,而 Alice 則獨自成組。
二、自定義分組規則
默認情況下,groupby 方法將使用元素對象本身作為 Map 的 key 進行分組,但是有時候可能需要自己定義自己的分組邏輯。可以通過使用 Collectors 中的 groupingBy 方法並傳遞自己的分類函數來實現這一點。示例代碼如下:
List people = Arrays.asList( new Person("John", 23), new Person("Alice", 25), new Person("Paul", 28) ); Map<Boolean, List> peopleByAge = people.stream() .collect(Collectors.groupingBy(p -> p.getAge() > 25)); System.out.println(peopleByAge); // 輸出結果:{false=[Person{name='John', age=23}, Person{name='Alice', age=25}], true=[Person{name='Paul', age=28}]}
在上面的代碼中,我們定義了一個分類函數,根據 Person 實例的 age 屬性判斷是否大於 25 ,如果大於,則分到 true 分組,否則分到 false 分組。運行後可以看到,分組的結果正確,分組規則也與預期一致。
三、多級分組
groupby 方法不僅可以進行單一的分組,還可以進行多級分組,可以根據傳遞給 groupingBy 方法的參數數量來進行分組。示例代碼如下:
List people = Arrays.asList( new Person("John", 23), new Person("Alice", 25), new Person("Paul", 28), new Person("John", 29), new Person("Alice", 21) ); Map<String, Map<Integer, List>> peopleByNameAndAge = people.stream() .collect(Collectors.groupingBy(Person::getName, Collectors.groupingBy(Person::getAge))); System.out.println(peopleByNameAndAge); // 輸出結果:{Alice={21=[Person{name='Alice', age=21}], 25=[Person{name='Alice', age=25}]}, John={23=[Person{name='John', age=23}], 29=[Person{name='John', age=29}]}, Paul={28=[Person{name='Paul', age=28}]}}
在上面的代碼中,我們對 person 列表進行了兩層分組,首先按照名字進行分組,然後在每個名字的分組中再按照年齡進行分組。此時,peopleByNameAndAge 對象的 key 值是名字,value 值則是按照年齡分組的 Map 對象。最終的結果顯示為了一個分層的 Map,其中每個元素都是一個二級 Map 對象。
四、將分組結果收集到指定類型的容器
默認情況下,groupby 方法將分組結果收集到一個 Map 中,如果需要將結果收集到特定類型的容器中,比如 Set 對象,可以使用 Collectors 中的 toCollection 方法。示例代碼如下:
List people = Arrays.asList( new Person("John", 23), new Person("Alice", 25), new Person("John", 28), new Person("Paul", 28), new Person("Alice", 21) ); Map<String, Set> peopleSetByName = people.stream() .collect(Collectors.groupingBy(Person::getName, Collectors.toCollection(TreeSet::new))); System.out.println(peopleSetByName); // 輸出結果:{Alice=[Person{name='Alice', age=21}, Person{name='Alice', age=25}], John=[Person{name='John', age=23}, Person{name='John', age=28}], Paul=[Person{name='Paul', age=28}]}
在上面的代碼中,我們使用 toCollection 方法將 Set 的實例化由默認的 HashSet 更改為了 TreeSet,以便讓收集到的結果按照自然排序的順序排列。
五、將分組結果按照大小排序
對於分組結果中的集合對象,可以使用 sorted 方法對其進行排序。示例代碼如下:
List people = Arrays.asList( new Person("John", 23), new Person("Alice", 25), new Person("John", 28), new Person("Paul", 28), new Person("Alice", 21) ); Map<String, List> peopleSortedByAge = people.stream() .collect(Collectors.groupingBy(Person::getName, Collectors.mapping(Function.identity(), Collectors.toList()))) .entrySet() .stream() .map(e -> new AbstractMap.SimpleEntry(e.getKey(), e.getValue().stream().sorted(Comparator.comparingInt(Person::getAge)).collect(Collectors.toList()))) .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)); System.out.println(peopleSortedByAge); // 輸出結果:{Alice=[Person{name='Alice', age=21}, Person{name='Alice', age=25}], John=[Person{name='John', age=23}, Person{name='John', age=28}], Paul=[Person{name='Paul', age=28}]}
在上面的代碼中,我們首先對 person 對象進行分組,並且使用 Collectors.mapping 方法將元素轉化為自身,然後轉化為 List 後再進行排序,最終得到每個姓名對應的已經排好序的 Person 對象的 List 對象。
六、將分組結果轉化為值對象
在分組過程中,可能需要將分組結果轉化為值對象,以便合併、比較和輸出等操作。這可以使用 Stream 中的 map 方法直接在分組後將 Map 中的 value 值映射為另一個值對象。示例代碼如下:
List people = Arrays.asList( new Person("John", 23), new Person("Alice", 25), new Person("John", 28), new Person("Paul", 28), new Person("Alice", 21) ); Map peopleGroupByName = people.stream() .collect(Collectors.groupingBy(Person::getName, Collectors.mapping(Function.identity(), Collectors.toList()))) .entrySet() .stream() .map(e -> new AbstractMap.SimpleEntry(e.getKey(), new Group(e.getValue()))) .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)); System.out.println(peopleGroupByName); // 輸出結果:{Alice=Group{people=[Person{name='Alice', age=25}, Person{name='Alice', age=21}]}, John=Group{people=[Person{name='John', age=23}, Person{name='John', age=28}]}, Paul=Group{people=[Person{name='Paul', age=28}]}}
在上面的代碼中,我們首先和之前一樣對 person 對象進行分組操作,但是這裡使用了 map 方法,將分組結果轉化為了自定義的 Group 類,輸出結果為以名字為 key,以 Group 類型的值對象為 value 的 Map 。
原創文章,作者:BLCVL,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/361497.html