当前位置: 首页 > news >正文

[改善Java代码]非稳定排序推荐使用List

我们知道Set与List的最大区别就是Set中的元素不可以重复(这个重复指的equals方法的返回值相等),其他方面则没有太大的区别了,在Set的实现类中有一个比较常用的类需要了解一下:TreeSet,该类实现了类默认排序为升序的Set集合,如果插入一个元素,默认会按照升序排列(当然是根据Comparable接口的compareTo的返回值确定排序位置了),不过,这样的排序是不是在元素经常变化的场景中也适用呢?我们来看例子:

 1 import java.util.SortedSet;
 2 import java.util.TreeSet;
 3 
 4 public class Client {
 5     public static void main(String[] args) {
 6         SortedSet<Person> set = new TreeSet<Person>();
 7         //身高180CM
 8         set.add(new Person(180));
 9         //身高175CM
10         set.add(new Person(175));
11         
12         for(Person p:set){
13             System.out.println("身高:"+p.getHeight());
14         }
15     }
16     
17     static class Person implements Comparable<Person>{
18         //身高
19         private int height;
20         
21         public Person(int _age){
22             height = _age;
23         }
24         
25 
26         public int getHeight() {
27             return height;
28         }
29 
30 
31         public void setHeight(int height) {
32             this.height = height;
33         }
34 
35         //按照身高排序
36         public int compareTo(Person o) {
37             return height - o.height;
38         }
39 
40     }
41 }

 

程序输出:

身高:175
身高:180

 

这没有问题,随着时间的推移,身高175cm的人长高了10cm,而180cm却保持不变,那排序的位置应该改变一下吧,看代码(只需修改main方法):

 1     public static void main(String[] args) {
 2         SortedSet<Person> set = new TreeSet<Person>();
 3         // 身高180CM
 4         set.add(new Person(180));
 5         // 身高175CM
 6         set.add(new Person(175));
 7         // 身高最矮的人大变身
 8         set.first().setHeight(185);
 9         for (Person p : set) {
10             System.out.println("身高:" + p.getHeight());
11         }
12     }

 

程序输出:

身高:185
身高:180

很可惜,竟然没有重新排序,偏离了我们的预期。这正是下面要说明的问题,SortedSet接口(TreeSet实现了该接口)只是定义了在给集合加入元素时将其进行排序,并不能保证元素修改后的排序结果,因此TreeSet使用于不变量的集合数据排序,比如String、Integer等类型,但不适用于可变量的排序,特别是不确定何时元素会发生变化的数据集合。 
原因知道了,那如何解决此类重排序问题呢?有两种方式: 

(1).Set集合重排序 
重新生成一个Set对象,也就是对原有的Set对象重排序,代码如下:

 1 import java.util.ArrayList;
 2 import java.util.SortedSet;
 3 import java.util.TreeSet;
 4 
 5 public class Client {
 6     public static void main(String[] args) {
 7         SortedSet<Person> set = new TreeSet<Person>();
 8         // 身高180CM
 9         set.add(new Person(180));
10         // 身高175CM
11         set.add(new Person(175));
12         // 身高最矮的人大变身
13         set.first().setHeight(185);
14         //set重排序
15         set = new TreeSet<Person>(new ArrayList<Person>(set));
       //set = new TreeSet(set);该构造函数只是原Set的浅拷贝,如果里面有相同的元素,是不会重新排序的
16 for (Person p : set) { 17 System.out.println("身高:" + p.getHeight()); 18 } 19 } 20 21 static class Person implements Comparable<Person> { 22 // 身高 23 private int height; 24 25 public Person(int _age) { 26 height = _age; 27 } 28 29 public int getHeight() { 30 return height; 31 } 32 33 public void setHeight(int height) { 34 this.height = height; 35 } 36 37 // 按照身高排序 38 public int compareTo(Person o) { 39 return height - o.height; 40 } 41 42 } 43 }

 

程序输出:

身高:180
身高:185

就一句话即可重新排序。可能有读者会问,使用TreeSet(SortedSet< E > s)这个构造函数不是可以更好地解决问题吗?不行,该构造函数只是原Set的浅拷贝,如果里面有相同的元素,是不会重新排序的。 
(2).彻底重构掉TreeSet,使用List解决问题 
我们之所以使用TreeSet是希望实现自动排序,即使修改也能自动排序,既然它无法实现,那就用List来代替,然后再使用Collections.sort()方法对List排序.看代码:

 1 import java.util.ArrayList;
 2 import java.util.Collections;
 3 import java.util.List;
 4 import java.util.TreeSet;
 5 
 6 public class Client {
 7     public static void main(String[] args) {
 8         List<Person> list = new ArrayList<Person>();
 9         // 身高180CM
10         list.add(new Person(180));
11         // 身高175CM
12         list.add(new Person(175));
13         // 身高最矮的人大变身
14         list.get(1).setHeight(185);
15         
16         //排序
17         Collections.sort(list);
18         for (Person p : list) {
19             System.out.println("身高:" + p.getHeight());
20         }
21     }
22 
23     static class Person implements Comparable<Person> {
24         // 身高
25         private int height;
26 
27         public Person(int _age) {
28             height = _age;
29         }
30 
31         public int getHeight() {
32             return height;
33         }
34 
35         public void setHeight(int height) {
36             this.height = height;
37         }
38 
39         // 按照身高排序
40         public int compareTo(Person o) {
41             return height - o.height;
42         }
43 
44     }
45 }

 

程序输出:

身高:180
身高:185

 

两种方法都可以解决我们的困境,到底哪一个是最优的呢?对于不变量的排序,例如直接量(也就是8个基本类型)、String类型等,推荐使用TreeSet,而对于可变量,例如我们自己写的类,可能会在逻辑处理中改变其排序关键值的,则建议使用List自行排序。 
又有问题了,如果需要保证集合中元素的唯一性,又要保证元素值修改后排序正确,那该如何处理呢?List不能保证集合中的元素唯一,它是可以重复的,而Set能保证元素唯一,不重复。如果采用List解决排序问题,就需要自行解决元素重复问题(若要剔除也很简单,转变为HashSet,剔除后再转回来)。若采用TreeSet,则需要解决元素修改后的排序问题,孰是孰非,就需要根据具体的开发场景来决定了。

 

相关文章:

  • Round 0: Regionals 2010 :: NEERC Eastern Subregional
  • 远程桌面卡
  • 二、ssh 协议:SSH 验证协议 —— 公钥认证
  • 安装程序时出现2502 2503错误解决方法
  • html5配合css3实现带提示文字的输入框(摆脱js)
  • AWS EC2 Run Command特性新增多重云脚本
  • 京东区域表整理
  • ubuntu下安装jdk
  • linux内核升级图文攻略
  • android adb指令
  • 【转】mysql的cardinality异常,导致索引不可用
  • jquery如何判断滚动条滚到页面底部并执行事件
  • ios 用户相册
  • [android] 练习PopupWindow实现对话框
  • %@ page import=%的用法
  • [nginx文档翻译系列] 控制nginx
  • Apache Pulsar 2.1 重磅发布
  • css属性的继承、初识值、计算值、当前值、应用值
  • Github访问慢解决办法
  • hadoop集群管理系统搭建规划说明
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • JavaScript对象详解
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • node-glob通配符
  • SpiderData 2019年2月25日 DApp数据排行榜
  • 闭包--闭包之tab栏切换(四)
  • 前端技术周刊 2019-02-11 Serverless
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 使用 Docker 部署 Spring Boot项目
  • 想写好前端,先练好内功
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • 2017年360最后一道编程题
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • ​一些不规范的GTID使用场景
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • %3cli%3e连接html页面,html+canvas实现屏幕截取
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (利用IDEA+Maven)定制属于自己的jar包
  • (收藏)Git和Repo扫盲——如何取得Android源代码
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (转)关于pipe()的详细解析
  • .gitignore
  • .Net Core和.Net Standard直观理解
  • .Net Redis的秒杀Dome和异步执行
  • .NET/C# 使窗口永不激活(No Activate 永不获得焦点)
  • .NET3.5下用Lambda简化跨线程访问窗体控件,避免繁复的delegate,Invoke(转)
  • .Net8 Blazor 尝鲜
  • .NET开发不可不知、不可不用的辅助类(一)
  • @RequestBody与@ResponseBody的使用
  • @Responsebody与@RequestBody
  • [Android 13]Input系列--获取触摸窗口
  • [BUAA软工]第一次博客作业---阅读《构建之法》
  • [caffe(二)]Python加载训练caffe模型并进行测试1