Java中List排序的3種方法!

作者 | 王磊
來源 | Java中文社群(ID:javacn666)
轉(zhuǎn)載請聯(lián)系授權(quán)(微信ID:GG_Stone)
在某些特殊的場景下,我們需要在 Java 程序中對 List 集合進行排序操作。比如從第三方接口中獲取所有用戶的列表,但列表默認是以用戶編號從小到大進行排序的,而我們的系統(tǒng)需要按照用戶的年齡從大到小進行排序,這個時候,我們就需要對 List 集合進行自定義排序操作了。
List 排序的常見方法有以下 3 種:
使用 Comparable 進行排序; 使用 Comparator 進行排序; 如果是 JDK 8 以上的環(huán)境,也可以使用 Stream 流進行排序。
下面我們分別來看各種排序方法的具體實現(xiàn)。
1.使用 Comparable 排序
按照本文設(shè)計的場景,我們需要創(chuàng)建一個包含了用戶列表的 List 集合,并按用戶的年齡從大到小進行排序,具體實現(xiàn)代碼如下:
public?class?ListSortExample?{
????public?static?void?main(String[]?args)?{
????????//?創(chuàng)建并初始化?List
????????List?list?=?new?ArrayList()?{{
????????????add(new?Person(1,?30,?"北京"));
????????????add(new?Person(2,?20,?"西安"));
????????????add(new?Person(3,?40,?"上海"));
????????}};
????????//?使用?Comparable?自定的規(guī)則進行排序
????????Collections.sort(list);
????????//?打印?list?集合
????????list.forEach(p?->?{
????????????System.out.println(p);
????????});
????}
}
//??以下?set/get/toString?使用的是?lombok?的注解
@Getter
@Setter
@ToString
class?Person?implements?Comparable<Person>?{
????private?int?id;
????private?int?age;
????private?String?name;
????public?Person(int?id,?int?age,?String?name)?{
????????this.id?=?id;
????????this.age?=?age;
????????this.name?=?name;
????}
????@Override
????public?int?compareTo(Person?p)?{
????????return?p.getAge()?-?this.getAge();
????}
}
以上代碼的執(zhí)行結(jié)果,如下圖所示:
本方法的核心代碼如下:
2.使用 Comparator 排序
Comparable 是類內(nèi)部的比較方法,而 Comparator 是排序類外部的比較器。使用 Comparator 比較器,無需修改原 Person 類,只需要擴充一個 Person 類的比較器就行了,Comparator 的實現(xiàn)方法有以下兩種:
新建 Comparator 比較器; 使用 Comparator 匿名類比較器。
其中,第二種實現(xiàn)方法要更簡潔一些,我們通過下面的具體代碼,來觀察一下二者的區(qū)別。
2.1 新建 Comparator 比較器
public?class?ListSortExample2?{
????public?static?void?main(String[]?args)?{
????????//?創(chuàng)建并初始化?List
????????List?list?=?new?ArrayList()?{{
????????????add(new?Person(1,?30,?"北京"));
????????????add(new?Person(2,?20,?"西安"));
????????????add(new?Person(3,?40,?"上海"));
????????}};
????????//?使用?Comparator?比較器排序
????????Collections.sort(list,?new?PersonComparator());
????????//?打印?list?集合
????????list.forEach(p?->?{
????????????System.out.println(p);
????????});
????}
}
/**
?*?新建?Person?比較器
?*/
class?PersonComparator?implements?Comparator<Person>?{
????@Override
????public?int?compare(Person?p1,?Person?p2)?{
????????return?p2.getAge()?-?p1.getAge();
????}
}
@Getter
@Setter
@ToString
class?Person?{
????private?int?id;
????private?int?age;
????private?String?name;
????public?Person(int?id,?int?age,?String?name)?{
????????this.id?=?id;
????????this.age?=?age;
????????this.name?=?name;
????}
}
以上代碼的執(zhí)行結(jié)果,如下圖所示:
本方法的核心實現(xiàn)代碼如下:
2.2 匿名類比較器
比較器 Comparator 可以使用更簡潔的匿名類的方式,來實現(xiàn)排序功能,具體實現(xiàn)代碼如下:
public?class?ListSortExample2?{
????public?static?void?main(String[]?args)?{
????????//?創(chuàng)建并初始化?List
????????List?list?=?new?ArrayList()?{{
????????????add(new?Person(1,?30,?"北京"));
????????????add(new?Person(2,?20,?"西安"));
????????????add(new?Person(3,?40,?"上海"));
????????}};
????????//?使用匿名比較器排序
????????Collections.sort(list,?new?Comparator()?{
????????????@Override
????????????public?int?compare(Person?p1,?Person?p2)?{
????????????????return?p2.getAge()?-?p1.getAge();
????????????}
????????});
????????//?打印?list?集合
????????list.forEach(p?->?{
????????????System.out.println(p);
????????});
????}
}
@Getter
@Setter
@ToString
class?Person?{
????private?int?id;
????private?int?age;
????private?String?name;
????public?Person(int?id,?int?age,?String?name)?{
????????this.id?=?id;
????????this.age?=?age;
????????this.name?=?name;
????}
}
以上代碼的執(zhí)行結(jié)果,如下圖所示:
3.使用 Stream 流排序
在 JDK 8 之后可以使用更加簡單的方法 Stream 流來實現(xiàn)排序功能,它的實現(xiàn)只需要一行代碼,具體實現(xiàn)如下:
public?class?ListSortExample3?{
????public?static?void?main(String[]?args)?{
????????//?創(chuàng)建并初始化?List
????????List?list?=?new?ArrayList()?{{
????????????add(new?Person(1,?30,?"北京"));
????????????add(new?Person(2,?20,?"西安"));
????????????add(new?Person(3,?40,?"上海"));
????????}};
????????//?使用?Stream?排序
????????list?=?list.stream().sorted(Comparator.comparing(Person::getAge).reversed())
????????????????.collect(Collectors.toList());
????????//?打印?list?集合
????????list.forEach(p?->?{
????????????System.out.println(p);
????????});
????}
????@Getter
????@Setter
????@ToString
????static?class?Person?{
????????private?int?id;
????????private?int?age;
????????private?String?name;
????????public?Person(int?id,?int?age,?String?name)?{
????????????this.id?=?id;
????????????this.age?=?age;
????????????this.name?=?name;
????????}
????}
}
其中 reversed() 表示倒序的意思,如果不使用此方法則是正序。
以上代碼的執(zhí)行結(jié)果,如下圖所示:
擴展:排序字段為 null
使用 Stream 進行排序時,如果排序的字段出現(xiàn) null 值就會導(dǎo)致異常發(fā)生,具體示例如下:
public?class?ListSortExample4?{
????public?static?void?main(String[]?args)?{
????????//?創(chuàng)建并初始化?List
????????List?list?=?new?ArrayList()?{{
????????????add(new?Person(30,?"北京"));
????????????add(new?Person(10,?"西安"));
????????????add(new?Person(40,?"上海"));
????????????add(new?Person(null,?"上海"));?//?年齡為?null?值
????????}};
????????//?按照[年齡]正序,但年齡中有一個?null?值
????????list?=?list.stream().sorted(Comparator.comparing(Person::getAge))
????????????????.collect(Collectors.toList());
????????//?打印?list?集合
????????list.forEach(p?->?{
????????????System.out.println(p);
????????});
????}
}
@Getter
@Setter
@ToString
class?Person?{
????private?Integer?age;
????private?String?name;
????public?Person(Integer?age,?String?name)?{
????????this.age?=?age;
????????this.name?=?name;
????}
}
以上代碼的執(zhí)行結(jié)果,如下圖所示:
想要解決上述問題,需要給 Comparator.comparing 傳遞第二個參數(shù):Comparator.nullsXXX,如下代碼所示:
public?class?ListSortExample4?{
????public?static?void?main(String[]?args)?{
????????//?創(chuàng)建并初始化?List
????????List?list?=?new?ArrayList()?{{
????????????add(new?Person(30,?"北京"));
????????????add(new?Person(10,?"西安"));
????????????add(new?Person(40,?"上海"));
????????????add(new?Person(null,?"上海"));
????????}};
????????//?按照[年齡]正序,但年齡中有一個?null?值
????????list?=?list.stream().sorted(Comparator.comparing(Person::getAge,
????????????????Comparator.nullsFirst(Integer::compareTo)))
????????????????.collect(Collectors.toList());
????????//?打印?list?集合
????????list.forEach(p?->?{
????????????System.out.println(p);
????????});
????}
}
@Getter
@Setter
@ToString
class?Person?{
????private?Integer?age;
????private?String?name;
????public?Person(Integer?age,?String?name)?{
????????this.age?=?age;
????????this.name?=?name;
????}
}
Comparator.nullsFirst 表示將排序字段中的 null 值放到集合最前面,如果想要將 null 值放到集合最后面可以使用 Comparator.nullsLast。
以上代碼的執(zhí)行結(jié)果,如下圖所示:
總結(jié)
本文介紹了 3 種 List 排序的方法,前兩種方法常用于 JDK 8 之前的版本,其中比較器 Comparator 有兩種實現(xiàn)的寫法,而在 JDK 8 之后的版本,就可以使用 Comparator.comparing 實現(xiàn)排序了,如果排序字段中可能出現(xiàn) null 值,要使用 Comparator.nullsXXX 進行排序處理(否則會報錯)。


