Java?Stream實現(xiàn)多字段分組groupingBy操作詳解
近期的項目里,遇到一個需求:對于含有多個元素的List<Person>
,按照其中的某幾個屬性進行分組,比如Persion::getAge
、Persion::getType
、Persion::getGender
等字段。下面就讓我們討論一下如何比較優(yōu)雅的按多字段進行分組groupingBy。
利用Stream進行分組
Stream
是Java8的一個新特性,主要用戶集合數(shù)據(jù)的處理,如排序、過濾、去重等等功能,這里我們不展開講解。本文主要講解的是利用Stream.collect()
來對List進行分組。
Person類Person.java:
public class Person { /** * id */ private Integer id; /** * 年齡 */ private Integer age; /** * 類型 */ private String type; /** * 姓名 */ private String name; /** * 性別 */ private String gender; public Integer getId() { return id; } public Person setId(Integer id) { this.id = id; return this; } public Integer getAge() { return age; } public Person setAge(Integer age) { this.age = age; return this; } public String getType() { return type; } public Person setType(String type) { this.type = type; return this; } public String getName() { return name; } public Person setName(String name) { this.name = name; return this; } public String getGender() { return gender; } public Person setGender(String gender) { this.gender = gender; return this; } }
1. 利用單個字段進行分組
如上面的Person類,如果對于其中的某一個字段進行分組(如gender),則比較簡單,我們可以利用Stream.collect()
和Collectors.groupingBy
結(jié)合,即可進行分組groupingBy,代碼如下:
public class TestGroupingBy { public static void main(String[] args) { List<Person> personList = Arrays.asList( new Person().setId(1).setAge(18).setType("student").setName("user - 1").setGender("male"), new Person().setId(2).setAge(20).setType("student").setName("user - 2").setGender("male"), new Person().setId(3).setAge(18).setType("student").setName("user - 3").setGender("male"), new Person().setId(4).setAge(18).setType("student").setName("user - 4").setGender("male"), new Person().setId(5).setAge(35).setType("teacher").setName("user - 5").setGender("male"), new Person().setId(6).setAge(35).setType("teacher").setName("user - 6").setGender("male"), new Person().setId(7).setAge(20).setType("student").setName("user - 7").setGender("male"), new Person().setId(8).setAge(20).setType("student").setName("user - 8").setGender("female"), new Person().setId(9).setAge(20).setType("student").setName("user - 9").setGender("female"), new Person().setId(10).setAge(20).setType("student").setName("user - 10").setGender("female") ); Map<String, List<Person>> groupingMap = personList.stream().collect(Collectors.groupingBy(Person::getGender)); }
其中的groupingMap
,類型為Map<String, List<Person>>
,第一個泛型為String
即分組字段(本例中為gender字段)的類型,第二個泛型為List<Person>
及分組結(jié)果的類型。
我們在Debug模式下運行代碼,可以看到groupingMap
數(shù)據(jù)如下:
可以看到personList
數(shù)據(jù)按照gender屬性被分成了兩組。
2. 利用多個字段進行分組
上面的例子是按單個字段分組,如果需要按照多個字段,如gender、age、type三個字段進行分組,同樣也可以可以利用Stream.collect()
和Collectors.groupingBy
結(jié)合的方式進行分組,不過該方式中調(diào)用Collectors.groupingBy
時需要多次嵌套調(diào)用,測試代碼如下:
public class TestGroupingBy { public static void main(String[] args) { List<Person> personList = Arrays.asList( new Person().setId(1).setAge(18).setType("student").setName("user - 1").setGender("male"), new Person().setId(2).setAge(20).setType("student").setName("user - 2").setGender("male"), new Person().setId(3).setAge(18).setType("student").setName("user - 3").setGender("male"), new Person().setId(4).setAge(18).setType("student").setName("user - 4").setGender("male"), new Person().setId(5).setAge(35).setType("teacher").setName("user - 5").setGender("male"), new Person().setId(6).setAge(35).setType("teacher").setName("user - 6").setGender("male"), new Person().setId(7).setAge(20).setType("student").setName("user - 7").setGender("male"), new Person().setId(8).setAge(20).setType("student").setName("user - 8").setGender("female"), new Person().setId(9).setAge(20).setType("student").setName("user - 9").setGender("female"), new Person().setId(10).setAge(20).setType("student").setName("user - 10").setGender("female") ); // 多字段嵌套分組 Map<String, Map<Integer, Map<String, List<Person>>>> groupingMap = personList.stream().collect( Collectors.groupingBy(Person::getGender, Collectors.groupingBy(Person::getAge, Collectors.groupingBy(Person::getType) ) ) ); } }
其中groupingMap
類型為Map<String, Map<Integer, Map<String, List<Person>>>>
,是一個嵌套了三層的Map,對應(yīng)的泛型String/Integer/String
分別為對應(yīng)分組字段的類型,最后一層Map的value類型為List<Person>
為實際分組后的數(shù)據(jù)集合類型,為方便查看數(shù)據(jù),特意按Json格式貼出數(shù)據(jù)如下:
{ "female": { "20": { "student": [ { "id": 8, "age": 20, "type": "student", "name": "user - 8", "gender": "female" }, { "id": 9, "age": 20, "type": "student", "name": "user - 9", "gender": "female" }, { "id": 10, "age": 20, "type": "student", "name": "user - 10", "gender": "female" } ] } }, "male": { "18": { "student": [ { "id": 1, "age": 18, "type": "student", "name": "user - 1", "gender": "male" }, { "id": 3, "age": 18, "type": "student", "name": "user - 3", "gender": "male" }, { "id": 4, "age": 18, "type": "student", "name": "user - 4", "gender": "male" } ] }, "20": { "student": [ { "id": 2, "age": 20, "type": "student", "name": "user - 2", "gender": "male" }, { "id": 7, "age": 20, "type": "student", "name": "user - 7", "gender": "male" } ] }, "35": { "teacher": [ { "id": 5, "age": 35, "type": "teacher", "name": "user - 5", "gender": "male" }, { "id": 6, "age": 35, "type": "teacher", "name": "user - 6", "gender": "male" } ] } } }
可以看到,原先的List數(shù)據(jù),按照gender/age/type
三個屬性,分成了三層的Map
,對于這種多層的Map
代碼上處理起來會有一些不方便。并且如果分組字段更多的話,所嵌套的Collectors.groupingBy
也會更加多,代碼書寫起來也不太優(yōu)雅。
下面將介紹另外一種按多字段分組的方法。
3. 利用Collectors.groupingBy與Function結(jié)合進行多字段分組
查看Collectors.groupingBy
API會發(fā)現(xiàn),其中一種用法是第一個參數(shù)為Function
,如下:
簡單翻譯一下就是:一種將輸入元素映射到鍵的分類函數(shù)。即需要定義一個函數(shù)Function,該函數(shù)將元素對象映射到一個鍵的集合里。代碼示例如下:
public class TestGroupingBy { public static void main(String[] args) { List<Person> personList = Arrays.asList( new Person().setId(1).setAge(18).setType("student").setName("user - 1").setGender("male"), new Person().setId(2).setAge(20).setType("student").setName("user - 2").setGender("male"), new Person().setId(3).setAge(18).setType("student").setName("user - 3").setGender("male"), new Person().setId(4).setAge(18).setType("student").setName("user - 4").setGender("male"), new Person().setId(5).setAge(35).setType("teacher").setName("user - 5").setGender("male"), new Person().setId(6).setAge(35).setType("teacher").setName("user - 6").setGender("male"), new Person().setId(7).setAge(20).setType("student").setName("user - 7").setGender("male"), new Person().setId(8).setAge(20).setType("student").setName("user - 8").setGender("female"), new Person().setId(9).setAge(20).setType("student").setName("user - 9").setGender("female"), new Person().setId(10).setAge(20).setType("student").setName("user - 10").setGender("female") ); // 定義一個函數(shù)Function,該函數(shù)將元素對象映射到一個鍵的集合里 Function<Person, List<Object>> compositeKey = person -> Arrays.asList(person.getGender(), person.getAge(), person.getType()); // 分組 Map<List<Object>, List<Person>> groupingMap = personList.stream().collect(Collectors.groupingBy(compositeKey, Collectors.toList())); } }
通過在Debug模式下運行代碼,可以看到groupingMap
的數(shù)據(jù)結(jié)構(gòu)如下:
groupingMap數(shù)據(jù)僅僅只有一層,但是其鍵值Key卻是一個List,里面包含了分組字段的值,如上圖中的male
、35
、teacher
是集合中屬性gender/age/type
分別是male
、35
、teacher
的元素集合。數(shù)據(jù)按Json格式貼出如下:
{ "[male, 35, teacher]": [ { "id": 5, "age": 35, "type": "teacher", "name": "user - 5", "gender": "male" }, { "id": 6, "age": 35, "type": "teacher", "name": "user - 6", "gender": "male" } ], "[female, 20, student]": [ { "id": 8, "age": 20, "type": "student", "name": "user - 8", "gender": "female" }, { "id": 9, "age": 20, "type": "student", "name": "user - 9", "gender": "female" }, { "id": 10, "age": 20, "type": "student", "name": "user - 10", "gender": "female" } ], "[male, 20, student]": [ { "id": 2, "age": 20, "type": "student", "name": "user - 2", "gender": "male" }, { "id": 7, "age": 20, "type": "student", "name": "user - 7", "gender": "male" } ], "[male, 18, student]": [ { "id": 1, "age": 18, "type": "student", "name": "user - 1", "gender": "male" }, { "id": 3, "age": 18, "type": "student", "name": "user - 3", "gender": "male" }, { "id": 4, "age": 18, "type": "student", "name": "user - 4", "gender": "male" } ] }
由于Map只有一層,用該方式分組的結(jié)果,對于我們業(yè)務(wù)也是比較友好,代碼里對數(shù)據(jù)處理起來也是比較方便的??梢钥吹剑瑥拇a書寫角度以及分組處理后得到的結(jié)果,該方法都是最優(yōu)雅的。
寫在最后
可以看到,如果分組字段只有一個,我們可以用比較簡單的利用Stream.collect()
和Collectors.groupingBy
進行處理,但對于多個字段的分組操作,建議還是用Collectors.groupingBy
和Function
進行處理。
到此這篇關(guān)于Java Stream實現(xiàn)多字段分組groupingBy操作詳解的文章就介紹到這了,更多相關(guān)Java Stream分組內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
websocket在springboot+vue中的使用教程
這篇文章主要介紹了websocket在springboot+vue中的使用教程,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-08-08spring boot整合RabbitMQ(Direct模式)
springboot集成RabbitMQ非常簡單,如果只是簡單的使用配置非常少,springboot提供了spring-boot-starter-amqp項目對消息各種支持。下面通過本文給大家介紹下spring boot整合RabbitMQ(Direct模式),需要的朋友可以參考下2017-04-04Android studio按鈕點擊頁面跳轉(zhuǎn)詳細步驟
在Android應(yīng)用程序中,頁面跳轉(zhuǎn)是非常常見的操作,下面這篇文章主要給大家介紹了關(guān)于Android studio按鈕點擊頁面跳轉(zhuǎn)的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-06-06