關於Java使用groupingBy分組數據亂序問題

這是對最近作的一個項目,其中一個知識點的總結。java

真實的業務場景就不說了,我來模擬下業務場景,足夠說明問題就好了。函數

假設我有個對象,存儲人員的基本信息,以下:測試

@AllArgsConstructor
@Data
@ToString
public class PersonInfo {
    private String name;
    private int age;
    private int sex; //0表示男性,1表示女性
}

添加一些測試數據,code

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)

咱們能夠看到,打印的順序和咱們添加的順序是同樣的。接口

如今咱們有個需求,按照性別進行分組。get

這個也不難,在 java8 環境下咱們可使用stream流的groupingBy很容易的實現,因而就有了下面的代碼,hash

Map<Integer, List<PersonInfo>> map = personInfoList.stream().collect(Collectors.groupingBy(PersonInfo::getSex));

groupingBy實現相似SQL語句的「Group By」字句功能,實現根據一些屬性進行分組並把結果存在Map實例。io

打印結果看看是怎樣的,java8

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類型的對象。

另外咱們知道HashMapLinkedHashMap的區別是後者經過雙向鏈表保證數據插入的順序和訪問的順序一致。 因而就有了下面的代碼,

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順序是同樣的。

相關文章
相關標籤/搜索