這是對最近做的一個項目,其中一個知識點的總結。
真實的業務場景就不說了,我來模擬下業務場景,足夠說明問題就行了。
假設我有個對象,存儲人員的基本信息,如下:
@AllArgsConstructor
@Data
@ToString
public class PersonInfo {
private String name;
private int age;
private int sex; //0表示男性,1表示女性
}
添加一些測試數據,
List<PersonInfo> personInfoList = new ArrayList<>();
personInfoList.add(new PersonInfo("tom", 32, 1));
personInfoList.add(new PersonInfo("jerry", 30, 1));
personInfoList.add(new PersonInfo("alice", 15, 0));
personInfoList.add(new PersonInfo("jack", 23, 1));
personInfoList.add(new PersonInfo("aya", 32, 0));
打印下看看,
personInfoList.forEach(e -> System.out.println(e));
PersonInfo(name=tom, age=32, sex=1)
PersonInfo(name=jerry, age=30, sex=1)
PersonInfo(name=alice, age=15, sex=0)
PersonInfo(name=jack, age=23, sex=1)
PersonInfo(name=aya, age=32, sex=0)
我們可以看到,打印的順序和我們添加的順序是一樣的。
現在我們有個需求,按照性別進行分組。
這個也不難,在 java8 環境下我們可以使用stream流的groupingBy
很容易的實現,於是就有了下面的代碼,
Map<Integer, List<PersonInfo>> map = personInfoList.stream().collect(Collectors.groupingBy(PersonInfo::getSex));
groupingBy
實現類似SQL語句的“Group By”字句功能,實現根據一些屬性進行分組並把結果存在Map實例。
打印結果看看是怎樣的,
map.forEach((key, value) -> System.out.println(key + ": " + value));
0: [PersonInfo(name=alice, age=15, sex=0), PersonInfo(name=aya, age=32, sex=0)]
1: [PersonInfo(name=tom, age=32, sex=1), PersonInfo(name=jerry, age=30, sex=1), PersonInfo(name=jack, age=23, sex=1)]
結果確實是按照要求分組了,但是PersonInfo對象的順序和我們之前添加的順序不一樣了,在有些業務場景下,這種結果往往是不允許的。(比如分頁查詢等)
爲什麼順序會亂的?
我們先來看看這個map是哪個類型的,
System.out.println(map.getClass().getSimpleName());
打印出來的結果是 HashMap
。
這個就解釋了爲啥順序被打亂了, HashMap
在存儲是數據時,是先用key做hash計算,然後根據hash的結果決定這條數據的位置,因爲hash本身是無序的,導致了讀出的順序是亂的。
知道了原因後,那就很容易想到解決方案了, groupingBy
有一個重載的方法,允許我們指定map進行操作,
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream)
注意看第二個參數, Supplier
是java8提供的一中函數接口類型,用於提供一個對象, 根據尖括號裏的定義,這裏需要提供一個Map
類型的對象。
另外我們知道HashMap
和LinkedHashMap
的區別是後者通過雙向鏈表保證數據插入的順序和訪問的順序一致。 於是就有了下面的代碼,
Map<Integer, List<PersonInfo>> map = personInfoList.stream().collect(Collectors.groupingBy(PersonInfo::getSex, LinkedHashMap::new, Collectors.toList()));
打印的結果是,
1: [PersonInfo(name=tom, age=32, sex=1), PersonInfo(name=jerry, age=30, sex=1), PersonInfo(name=jack, age=23, sex=1)]
0: [PersonInfo(name=alice, age=15, sex=0), PersonInfo(name=aya, age=32, sex=0)]
可以看到打印的順序和原來的list順序是一樣的。