1. 程式人生 > 其它 >Map集合(List<Map>)或者實體類集合(List<Entity>)該如何排序?

Map集合(List<Map>)或者實體類集合(List<Entity>)該如何排序?

技術標籤:JavaSEjava快速排序arraylistjavase

如果你向神求助,說明你相信了神的力量;如果神沒有幫助你,說明神相信了你的力量。

文章目錄

前言

排序”這兩個字想必大家如果學過演算法的話恐怕都不會陌生。但是我們在演算法的學習中往往都是用一個整數陣列來進行排序,而在實際工作中,我們往往碰到的都是實體類集合或者Map集合的形式。那麼,在這種情況下,我們該如何進行排序呢?

前期準備

實體類

我們先來準備一個實體類,待會用於生成實體類集合再排序:

package listsort;
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; /** * @ClassName Entity * @Description 實體類 - 人類 * @Author 古闕月 * @Date 2020/12/27 * @Version 1.0 */ //lombok外掛的註解 @Data // 若未使用lombok外掛,請自行生成getter、setter以及toString方法 @AllArgsConstructor
// 若未使用lombok外掛,請自行生成有參構造方法 @NoArgsConstructor // 若未使用lombok外掛,請自行生成無參構造方法 @Accessors(chain = true) // 開啟鏈式程式設計 public class Person { /** * 姓名 */ private String name; /** * 年齡 */ private int age; }

生成集合工具類

再來一個可以批量生成集合的工具類:

package listsort;

import java.util.ArrayList;
import java.util.HashMap; import java.util.List; import java.util.Map; /** * @ClassName GainList * @Description 獲取集合的工具類 * @Author 古闕月 * @Date 2020/12/27 23:43 * @Version 1.0 */ public class GainList { /** * 隨機生成包含num個map的List集合 * @param num * @return */ public static List<Map> getMapList(int num) { List<Map> mapList = new ArrayList<>(); for (int i = 0; i < num; i++) { // 隨機生成 0-100 直接的整數 int j = (int) (Math.random() * 101); Map map = new HashMap(); map.put("name", "李華"+j); map.put("age", j); mapList.add(map); } return mapList; } /** * 隨機生成包含num個人類的List集合 * @param num * @return */ public static List<Person> getPersonList(int num) { List<Person> personList = new ArrayList<>(); for (int i = 0; i < num; i++) { // 隨機生成 0-100 直接的整數 int j = (int) (Math.random() * 101); Person person = new Person(); person.setName("李華"+j); person.setAge(j); personList.add(person); } return personList; } }

方式一

實體類集合排序

如果讓你們將一個包含實體類人類的集合按照年齡升序排序,你們會怎麼做?

可能很多小夥伴會像下面的程式碼一樣先得到一個年齡升序的陣列(反正我之前就是這麼做的,結果挨批了),然後再根據這個年齡陣列將集合中的實體類一個個抽出來:

package listsort;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName ListEntitySort
 * @Description List<entity> 排序 - 老方法
 * @Author 古闕月
 * @Date 2020/12/25
 * @Version 1.0
 */
public class ListEntitySort {
    public static void main(String[] args) {

        // 用於得到排序後返回的list
        List<Person> sortList = new ArrayList<>();

        /*
         * 隨機生成包含3個人類的List集合
         */
        List<Person> personList = GainList.getPersonList(3);
        System.out.println("排序前:" + personList);

        // 用於獲取年齡陣列
        int[] ageArr = new int[personList.size()];
        // 遍歷集合,獲取包含年齡的陣列
        for (int i = 0; i < personList.size(); i++) {
            ageArr[i] = personList.get(i).getAge();
        }

        // 將包含年齡的陣列排序 - 升序
        Arrays.sort(ageArr);

        for (int i = 0; i < ageArr.length; i++) {

            for (int j = 0; j < personList.size(); j++) {

                if (ageArr[i] == personList.get(j).getAge()) {

                    // 新增
                    sortList.add(personList.get(j));
                    // 移除,減少下次遍歷次數
                    personList.remove(j);
                    // 防止報錯
                    j--; 
                }
            }
        }
        System.out.println("排序後:" + sortList);
    }
}

執行得:
在這裡插入圖片描述

Map集合排序

Map集合排序的方式一其實跟實體類集合是一樣的,這裡我們來個降序排序:

package listsort;

import java.util.*;

/**
 * @ClassName ListMapSort
 * @Description List<map> 排序 - 老方法
 * @Author 古闕月
 * @Date 2020/12/25 16:40
 * @Version 1.0
 */
public class ListMapSort {
    public static void main(String[] args) {

        // 用於得到排序後返回的list
        List<Map> sortList = new ArrayList<>();

        /*
         * 隨機生成包含3個map的List集合
         */
        List<Map> mapList = GainList.getMapList(3);
        System.out.println("排序前:" + mapList);

        // 用於獲取年齡陣列
        Integer[] ageArr = new Integer[mapList.size()];
        // 遍歷集合,獲取 包含年齡的陣列
        for (int i = 0; i < mapList.size(); i++) {
            ageArr[i] = (int) mapList.get(i).get("age");
        }

        // 將包含年齡的陣列排序 - 降序
        Arrays.sort(ageArr, Collections.reverseOrder());

        for (int i = 0; i < ageArr.length; i++) {

            for (int j = 0; j < mapList.size(); j++) {

                if (ageArr[i] == (int)mapList.get(j).get("age")) {

                    // 新增
                    sortList.add(mapList.get(j));
                    // 移除,減少下次遍歷次數
                    mapList.remove(j);
                    // 防止報錯
                    j--; 
                }
            }
        }
        System.out.println("排序後:" + sortList);
    }
}

執行得:
在這裡插入圖片描述

方式二

之前方式一的方式雖然可以實現效果,但是我們可以很容易發現兩大缺點:

  1. 程式碼量太多導致可讀性較差,不易於維護。
  2. 空間複雜度較高,new了一個新的集合。

那麼有沒有什麼方法可以改進呢? 當然有了,不然我寫這篇部落格幹嘛?

Java的api其實已經給我們提供方法了,如以實體類集合排序為例:

package listsort;

import java.util.*;

/**
 * @ClassName ListEntitySort2
 * @Description List<entity> 排序
 * @Author 古闕月
 * @Date 2020/12/25
 * @Version 1.0
 */
public class ListEntitySort2 {
    public static void main(String[] args) {

        /*
         * 隨機生成包含3個人類的List集合
         */
        List<Person> personList = GainList.getPersonList(3);
        System.out.println("排序前:" + personList);

        /**
         * 將包含3個person實體的List集合按照年齡由低到高排序
         */
        Collections.sort(personList, new Comparator<Person>() {
            @Override
            public int compare(Person p1, Person p2) {
                
                int age1 = p1.getAge();
                int age2 = p2.getAge();

                if (age1 > age2) return 1;
                if (age1 < age2) return -1;

                return 0;
            }
        });

        System.out.println("排序後:" + personList);
    }
}

是不是感覺程式碼瞬間簡潔了許多?執行得:
在這裡插入圖片描述
排序完成,very good!

比較

通過以上對比,我們可以很明顯的發現方式二的兩大優點:

  1. 程式碼可讀性較好,易於維護。
  2. 空間複雜度較低。

那麼,既然是程式,必定逃不過一個問題,那就是速率問題,或者說是時間複雜度

讓我們來測試一下吧!!!

我們以Map集合排序為例,如用方式一對1000個Map進行排序:

package listsort;

import java.util.*;

/**
 * @ClassName ListMapSort
 * @Description List<map> 排序 - 老方法
 * @Author 古闕月
 * @Date 2020/12/25 16:40
 * @Version 1.0
 */
public class ListMapSort {
    public static void main(String[] args) {

        // 獲取當前系統的毫秒數
        long begin = System.currentTimeMillis();

        // 用於得到排序後返回的list
        List<Map> sortList = new ArrayList<>();

        /*
         * 隨機生成包含num個map的List集合
         */
        int num = 1000;
        List<Map> mapList = GainList.getMapList(num);
        System.out.println("排序前:" + mapList);

        // 用於獲取年齡陣列
        int[] ageArr = new int[mapList.size()];
        // 遍歷集合,獲取 包含年齡的陣列
        for (int i = 0; i < mapList.size(); i++) {
            ageArr[i] = (int) mapList.get(i).get("age");
        }

        // 將包含年齡的陣列排序 - 升序
        Arrays.sort(ageArr);
     
        for (int i = 0; i < ageArr.length; i++) {

            for (int j = 0; j < mapList.size(); j++) {

                if (ageArr[i] == (int)mapList.get(j).get("age")) {

                    // 新增
                    sortList.add(mapList.get(j));
                    // 移除,減少下次遍歷次數
                    mapList.remove(j);
                    j--; // 防止報錯
                }
            }
        }
        System.out.println("排序後:" + sortList);

        // 獲取當前系統的毫秒數
        long end = System.currentTimeMillis();
        System.out.println("方法一排序含" + num + "個map的List集合,耗時:" + (double)(end - begin) / 1000 + "s");
    }
}

執行得:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-PFWg7uyZ-1609996372556)(C:\Users\古闕月\AppData\Roaming\Typora\typora-user-images\image-20210107081455091.png)]
我們可以發現一號選手用時0.048秒,下面有請二號選手:

package listsort;

import java.util.*;

/**
 * @ClassName ListMapSort2
 * @Description List<map> 排序
 * @Author 古闕月
 * @Date 2020/12/25
 * @Version 1.0
 */
public class ListMapSort2 {
    public static void main(String[] args) {

        long begin = System.currentTimeMillis();

        /*
         * 隨機生成包含num個map的List集合
         */
        int num = 1000;
        List<Map> mapList = GainList.getMapList(num);
        System.out.println("排序前:" + mapList);

        /**
         * 將包含3個map的List集合按照年齡由低到高排序
         */
        Collections.sort(mapList, new Comparator<Map>() {
            @Override
            public int compare(Map o1, Map o2) {
                
                int age1 = (int) o1.get("age");
                int age2 = (int) o2.get("age");

                if (age1 > age2) return 1;
                if (age1 < age2) return -1;

                return 0;
            }
        });

        System.out.println("排序後:" + mapList);

        long end = System.currentTimeMillis();
        System.out.println("方法一排序含" + num + "個map的List集合,耗時:" + (double)(end - begin) / 1000 + "s");

    }
}

執行得:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-19zxcy42-1609996372558)(C:\Users\古闕月\AppData\Roaming\Typora\typora-user-images\image-20210107081650045.png)]
這個時候,我們驚喜的發現,二號選手用時更短,僅為0.015秒,不信你多測幾次便知。

哪怕,我們對方式一進行優化,如將 包含年齡的陣列排序 的排序方法這一行程式碼

// 將包含年齡的陣列排序 - 升序
Arrays.sort(ageArr);

改為快速排序

// 快速排序
QuickSort.quickSort(ageArr, 0, ageArr.length - 1);

雖然速率有所波動,但有時耗時會很少,有時會更多,效果仍是差強人意。所以這裡我們可以發現方式二的第三大優點:

  1. 時間複雜度低,也就是速率比較快。

至於快速排序速率為什麼會波動以及快速排序的程式碼,大家可以參考我的下面這篇部落格中的第四章節<快速排序>,然後自行探究驗證:
肝了幾萬字,送給看了《演算法圖解》卻是主攻Java的你和我(上篇)