在Java中,对集合或Map中元素进行排序或过滤是一个频繁操作。这里以List为例介绍下如何在集合中实现元素的排序和过滤功能。对于非List元素(Set、Map)等,一方面可以参考List使用类似的方法,另一方面可以将其转换成List并执行相关方法,关于Set或Map等与List的转换,可以参考笔者之前的文章。

排序

在日常的开发中,开发人员经常使用数据库的排序能力对待查询的数据进行排序。但是,业务上也会遇到没有数据库的场景,如数据存储在文件中,且数据量不大,这个时候就需要在内存中排序。对于内存排序,在算法领域也将其称之为内部排序。常见的内部排序算法有:快速排序、冒泡排序、归并排序、堆排序等。在日常的开发工作中,不推荐自己实现一个排序算法,而是推荐优先使用类库已提供的排序方法。对于List中元素排序,这里推荐使用Collections.sort方法或Stream.sorted方法。

使用Collections.sort方法

JDK提供Collections.sort方法用于实现集合元素排序。查看该方法源码如下:

// Collections.java
public static <T extends Comparable<? super T>> void sort(List<T> list) {list.sort(null);
}

查看源码可知,Collections.sort方法只能用于List。所以,对于Set或Map,则需先先将其转换成List方可使用。进一步深入List源码实现:

// List.java
default void sort(Comparator<? super E> c) {Object[] a = this.toArray();Arrays.sort(a, (Comparator) c);ListIterator<E> i = this.listIterator();for (Object e : a) {i.next();i.set((E) e);}
}
// Arrays.java
public static <T> void sort(T[] a, Comparator<? super T> c) {if (c == null) {sort(a);} else {if (LegacyMergeSort.userRequested)legacyMergeSort(a, c);elseTimSort.sort(a, 0, a.length, c, null, 0, 0);}
}

List在实现排序时,复用了数组的排序。而数组在实现排序时,使用归并排序实现元素排序。这里不再进一步深入归并排序的源码,有兴趣的同学可以自行查看相关源码。
接下来将给出使用示例:

List<Integer> originalList = new ArrayList<>();
originalList.add(1);
originalList.add(100);
originalList.add(50);
originalList.add(20);
originalList.add(35);
originalList.add(2);
Collections.sort(originalList, Integer::compareTo);
// 排序后,List的值为:
// [1, 2, 20, 35, 50, 100]

由于Integer实现了Comparable接口,所以上面的代码还可以进一步简写成Collections.sort(originalList)。当不指定比较器时,该方法会调用集合中元素所属类实现的排序方法。
进一步分析,这里并没有指定是升序还是降序时,可以看到当不指定生效还是降序时,默认是升序。这与数据库的默认排序规则是一样的。但在实际的应用中,有些场景也需要降序。对于List,Collections并不支持指定降序,有两种方法可以实现,一种是实现一个降序的比较器,另一种则是将这个已升序的数组进行翻转。这里给出Collections提供基于翻转实现降序的示例:

List<Integer> originalList = new ArrayList<>();
originalList.add(1);
originalList.add(100);
originalList.add(50);
originalList.add(20);
originalList.add(35);
originalList.add(2);
Collections.sort(originalList, Integer::compareTo);
Collections.reverse(originalList);
// 排序后,List的值为:
// [100, 50, 35, 20, 2, 1]

使用Stream.sorted方法

Java 8 新增流处理能力,可以使用Stream.sorted方法实现集合中元素排序。示例代码如下:

List<Integer> originalList = new ArrayList<>();
originalList.add(1);
originalList.add(100);
originalList.add(50);
originalList.add(20);
originalList.add(35);
originalList.add(2);
List<Integer> sortedList = originalList.stream().sorted(Integer::compareTo).collect(Collectors.toList());
// 排序后,List的值为:
// [1, 2, 20, 35, 50, 100]

多字段排序

上面讨论的是单字段的排序,适用集合中存储的元素是Integer、Long、String等基本类型的包装器类型场景,对于元素是对象场景,特别需要对对象中多个字段进行排序时,其处理略有不同。
对多字段排序,本质上是实现一个多字段的比较器。假设要从候选人中选取合格者,候选人定义如下:

class Candidate {int id;int programGrade;int technologyGrade;public Candidate(int id, int programGrade, int technologyGrade) {this.id = id;this.programGrade = programGrade;this.technologyGrade = technologyGrade;}public int getTechnologyGrade() {return this.technologyGrade;}public int getProgramGrade() {return this.programGrade;}public int getId() {return this.id;}
}

对候选人,假设有如下选取规则: 优选选取技术面试分数较高者;如果技术面试分数相同,则优先录取编程考试分数较高者;如果编程考试分数还相同,则录取id较小者。分析该应用场景,需要对多字段进行排序,具体来说,优先基于技术面试分数倒排,然后基于编程考试分数倒排,最后基于id升序排序(id一定不同)。使用Collections.sort和Stream.sorted分别实现该场景排序,示例代码如下:

public static void sortCandidateByCollections(List<Candidate> candidateList) {Collections.sort(candidateList, (o1, o2) -> {if (o1.technologyGrade != o2.technologyGrade) {return o2.technologyGrade - o1.technologyGrade;}if (o1.programGrade != o2.programGrade) {return o2.programGrade - o1.programGrade;}return o1.id - o2.id;});
}public static List<?> sortCandidateByStream(List<Candidate> candidateList) {return candidateList.stream().sorted((o1, o2) -> {if (o1.technologyGrade != o2.technologyGrade) {return o2.technologyGrade - o1.technologyGrade;}if (o1.programGrade != o2.programGrade) {return o2.programGrade - o1.programGrade;}return o1.id - o2.id;}).collect(Collectors.toList());
}public static List<?> sortCandidateByStreamInSimple(List<Candidate> candidateList) {return candidateList.stream().sorted(Comparator.comparing(Candidate::getTechnologyGrade).thenComparing(Candidate::getProgramGrade).thenComparing(Candidate::getId)).collect(Collectors.toList());
}

Collections.sort和Stream.sorted对比

从升序、降序,单字段、多字段场景,可以看到Collections.sort和Stream.sorted本质上都是对比较器的使用。但是两者在使用上有差异:
(1) Collections.sort会改变原来的数据,Stream.sorted不会改变原来的数据。如果期望Stream.sorted生成新的数据,还需另外处理。
(2) Collections.sort是静态方法,Stream.sorted是实例方法,两个方法的生命周期不同。
(3) Stream.sorted提供更多的简洁写法,且多数据场景下,支持并发处理。

过滤

除了排序,还有一种场景就是从集合中过滤出感兴趣的数据(也可理解成过滤掉不感兴趣的数据)。Java 8 之前通过遍历集合的方式实现,Java 8 之后(包含Java 8),可以使用Stream的filter方法实现。示例代码如下:

public static List<String> filterByForEach(List<String> languageList) {List<String> result = new ArrayList<>();for (String language : languageList) {if ("java".equals(language)) {result.add(language);}}return result;
}public static List<String> filterByStream(List<String> languageList) {return languageList.stream().filter(line -> !"java".equals(line)).collect(Collectors.toList());
}

参考

https://blog.csdn.net/w727655308/article/details/109959749 Java 实现多字段排序
https://juejin.cn/post/7083318717748084743 Java stream 多字段排序踩坑
https://segmentfault.com/a/1190000020158145 Java8 Streams filter 使用

Java集合或Map中元素排序及过滤相关推荐

  1. 浅入深出之Java集合框架(中)

    Java中的集合框架(中) 由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,如果已经有java基础的小伙伴可以直接跳到浅入深出之Java集合框架(下). ...

  2. Java小知识-----Map 按Key排序和按Value排序

    Map排序的方式有很多种,这里记录下自己总结的两种比较常用的方式:按键排序(sort by key), 按值排序(sort by value). 1.按键排序 jdk内置的java.util包下的Tr ...

  3. java集合之Map

    Map和Collecton比较 Collection中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储. Map中的集合,元素是成对存在的(理解为夫妻).每个元素由键与 ...

  4. Java集合Collection接口中的常用方法演示

    Java集合Collection接口中的常用方法演示 添加 add(Objec tobj) 和 addAll(Collection coll) 获取有效元素的个数 int size() 清空集合 vo ...

  5. C++与C语言中有关数组中元素排序

    C++与C语言中有关数组中元素排序 C语言中 ​ #include<stdio.h> #define n 4 int main(){ int a[n]; int i,j,temp; for ...

  6. Java集合查找Map,Java集合框架中Map接口的使用

    在我们常用的Java集合框架接口中,除了前面说过的Collection接口以及他的根接口List接口和Set接口的使用,Map接口也是一个经常使用的接口,和Collection接口不同,Map接口并不 ...

  7. java map removeall_Java删除Map中元素

    前言: 关于Java从Map中删除元素的使用,可以使用删除单个元素的事实Map.remove. 示例: 初始化一个Map对象 Map map = new HashMap<>(); map. ...

  8. java map移除_Java根据条件删除Map中元素

    今天在写程序过程中,需要根据判断条件删除一个Map中的相应数据,我自然而然想到可以通过调用Map中的remove(Object key)函数进行删除:代码如下: public Map processM ...

  9. java map合并_详解Java8合并两个Map中元素的正确姿势

    1. 介绍 本入门教程将介绍Java8中如何合并两个map. 更具体说来,我们将研究不同的合并方案,包括Map含有重复元素的情况. 2. 初始化 我们定义两个map实例 private static ...

最新文章

  1. 装饰器方式的添加路由
  2. Linux非阻塞IO(二)网络编程中非阻塞IO与IO复用模型结合
  3. 用一条sql语句删除表中所相同记录
  4. Python 技术篇-百度语音API鉴权认证获取Access Token实例演示
  5. java trackid_Java Preference.getContext方法代码示例
  6. nVIDIA显卡命名规律
  7. [css] 如何形成BFC?
  8. 西北大学调剂计算机,西北大学信息科学与技术学院2020年硕士考研预调剂公告...
  9. Original error was: DLL load failed while importing _multiarray_umath: 找不到指定的模块
  10. 基于Nginx的Wesocket负载均衡
  11. 好朋友的爬虫共享资料,真佩服
  12. XMLHttpRequest发送POST请求
  13. Python学习手册之类和继承
  14. 自动化测试Linux和fio,一种基于fio实现的SSD性能自动化测试方法与流程
  15. 健康管理师考试重点详解!(基础知识篇)
  16. thinkphp 下实现繁简体汉字转换
  17. SPSS-季节性分析
  18. 使用adb指令往机顶盒上安装应用
  19. 离线安装vscode
  20. 如何从AD中彻底删除Skype For Business(下篇)

热门文章

  1. C++的strcmp
  2. 网络服务器怎么修改ip,怎么改变自己的IP地址?
  3. Java工作4年来应聘要16K最后没要,细节如下。。。
  4. Android打开隐藏的应用
  5. C语言主函数返回值含义
  6. 【SpringBoot】十四、常见注解(场景及源码)
  7. 一字节anti创建进程线程等回调
  8. 目前主流的app开发方式
  9. JVM内存模型以及JVM内存模型图
  10. GitFlow 代码管理模型实战