由浅入深,大道至简。

一、炼精化气:int数组冒泡排序

什么,听说你修真忘了秘籍?这就带你回忆回忆,冒泡排序图解:

理解了冒泡排序流程之后,我们随手写一个int类型数组的冒泡排序:

public static void main(String[] args) {int[] array = {5, 6, 7, 12, 36, 9, 10, 4};for (int i = 0; i < array.length; i++) {for (int j = i; j < array.length; j++) {if (array[i] > array[j]) {int temp = array[i];array[i] = array[j];array[j] = temp;}}}System.out.println(Arrays.toString(array));
}

控制台输出结果:

[4, 5, 6, 7, 9, 10, 12, 36]Process finished with exit code 0

输出结果与我们预想的一致。

but,请思考一下,如果说我们要给String类型的字符串进行冒泡排序,又该怎么解决呢?

String[] array = {"100", "13", "21", "12", "43", "90", "01", "100"};

我们先试着把int类型的数字变成字符串,这样就能获得一个字符串数组,然后通过转型,又将字符串变成数字,像之前那样进行比较。

public static void main(String[] args) {String[] array = {"100", "13", "21", "12", "43", "90", "01", "100"};for (int i = 0; i < array.length; i++) {for (int j = i; j < array.length; j++) {int a = Integer.parseInt(array[i]);// String转int类型int b = Integer.parseInt(array[j]);if (a > b) {// 比较int类型的两个数String temp = array[i];array[i] = array[j];array[j] = temp;}}}System.out.println(Arrays.toString(array));
}

输出结果

[01, 12, 13, 21, 43, 90, 100, 100]Process finished with exit code 0

我猜你会想,这不是脱了裤子放屁,最后还是转成了int进行比较吗?

别着急,这是为之后转换真正的字符串进行铺垫。

二、炼气化神:String数组使用API进行比较——compareTo

我们直接查看String给我们提供的比较API源码:

public int compareTo(String anotherString) {int len1 = value.length;int len2 = anotherString.value.length;int lim = Math.min(len1, len2);char v1[] = value;char v2[] = anotherString.value;int k = 0;while (k < lim) {char c1 = v1[k];char c2 = v2[k];if (c1 != c2) {return c1 - c2;}k++;}return len1 - len2;
}

通过源码我们不难发现,原理和之前我们的写法有着惊人的相似!

API首先获取两个字符串的长度,通过Math.min函数取其中最小的长度,将字符串转换为char类型的数组,再将char数组进行遍历比较,由此制定了String类型的比较规则。

我们使用compareTo方法玩一把:

public static void main(String[] args) {String[] array = {"dcba", "acdb", "cadb", "cabd", "abcd", "adcb", "cbad", "dabc"};for (int i = 0; i < array.length; i++) {for (int j = i; j < array.length; j++) {if (array[i].compareTo(array[j]) > 0) {String temp = array[i];array[i] = array[j];array[j] = temp;}}}System.out.println(Arrays.toString(array));
}

输出结果:

[abcd, acdb, adcb, cabd, cadb, cbad, dabc, dcba]Process finished with exit code 0

这样的排序结果还是令人比较满意的。按照首字母排列。

那我们再多测试几组数据看看(此处忽略代码结构,仅观察参数和输出结果):

String[] array = {"a", "c", "d", "b", "cda", "ba", "cd", "ab"};
[a, ab, b, ba, c, cd, cda, d]Process finished with exit code 0
String[] array = {"100", "13", "21", "12", "43", "90", "01", "100"};
[01, 100, 100, 12, 13, 21, 43, 90]Process finished with exit code 0

我们发现结果开始有点不对劲!似乎不是我们期望的结果了。

由此我们需要重新了解compareTo方法的特性!

通过观察测试结果,再结合compareTo方法的源码,我们不难看出,这种比较方式,类似最左前缀原理。从左到右,按照字符ASCALL码大小进行排序,而完全与字符串的长度无关,也就是字符串的长度不被考虑进入比较范围。

因此,在以后使用此方法的过程中,一定要注意此方法不适用于比较长度不一的数字型字符串。

现在我们搞明白了字符串的比较规则,下一步我们要研究研究如何比较对象。

三、炼神还虚:将对象排序——实现Comparable接口

什么?你没有对象?别闹!

通常情况下,当我们谈到给对象排序,脑子里首先想到的应该是让对象实体实现Comparable接口,然后重写compareTo方法。

说干就干,我们指定指定以id进行排序:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Comparable {private String id;private String username;private String password;private int age;@Overridepublic int compareTo(Object o) {return o instanceof User ? this.id.compareTo(((User) o).id) : 0;}}
public static void main(String[] args) {List<User> userList = new ArrayList<>();//                     id    username  password  ageuserList.add(new User("009", "admin5", "123123", 12));userList.add(new User("005", "admin4", "321332", 12));userList.add(new User("003", "admin3", "123123", 12));userList.add(new User("004", "admin2", "123123", 15));userList.add(new User("002", "admin1", "123123", 16));Collections.sort(userList);userList.forEach(user -> System.out.println(user));
}

输出结果:

User(id=002, username=admin1, password=123123, age=16)
User(id=003, username=admin3, password=123123, age=12)
User(id=004, username=admin2, password=123123, age=15)
User(id=005, username=admin4, password=321332, age=12)
User(id=009, username=admin5, password=123123, age=12)Process finished with exit code 0

没错了,是我们要的根据id排序!

接下来,我们来仿写一个Comparable接口。

四、炼虚合道:仿写Comparable接口

先写一个接口:

public interface MyComparable<T> {int myCompareTo(T o);
}

给实体类指定比较规则:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements MyComparable {private String id;private String username;private String password;private int age;@Overridepublic int myCompareTo(Object o) {return o instanceof User ? this.id.compareTo(((User) o).id) : 0;}
}

测试自定义排序:

public class Test {public static void main(String[] args) {List<User> userList = new ArrayList<>();//                     id    username  password  ageuserList.add(new User("009", "admin5", "123123", 12));userList.add(new User("005", "admin4", "321332", 12));userList.add(new User("003", "admin3", "123123", 12));userList.add(new User("004", "admin2", "123123", 15));userList.add(new User("002", "admin1", "123123", 16));mysort(userList);// 排序userList.forEach(user -> System.out.println(user));}// 指定排序方式public static <T extends MyComparable<? super T>> void mysort(List<T> users) {for (int i = 0; i < users.size(); i++) {for (int j = i; j < users.size(); j++) {T a = users.get(i);T b = users.get(j);if (a.myCompareTo(b) > 0)Collections.swap(users, i, j);}}}}

输出结果:

User(id=002, username=admin1, password=123123, age=16)
User(id=003, username=admin3, password=123123, age=12)
User(id=004, username=admin2, password=123123, age=15)
User(id=005, username=admin4, password=321332, age=12)
User(id=009, username=admin5, password=123123, age=12)Process finished with exit code 0

不错不错, 自定义的Comparable接口也能满足我们的需求。

可即便是这样的排序方式,不禁让人产生思考:若是换一种排序规则,岂不是又要修改代码重写compareTo方法?

五、超凡入圣:将对象排序——实现Comparator接口

为了解决上面抛出的问题,我们可以使用Comparator接口来解决。

使用匿名内部类可以灵活地设置排序规则

public static void main(String[] args) {List<User> userList = new ArrayList<>();//                     id    username  password  ageuserList.add(new User("009", "admin5", "123123", 12));userList.add(new User("005", "admin4", "321332", 12));userList.add(new User("003", "admin3", "123123", 12));userList.add(new User("004", "admin2", "123123", 15));userList.add(new User("002", "admin1", "123123", 16));// 排序userList.sort(new Comparator<User>() {@Overridepublic int compare(User o1, User o2) {return o1.getId().compareTo(o2.getId());}});userList.forEach(user -> System.out.println(user));
}

输出结果:

User(id=002, username=admin1, password=123123, age=16)
User(id=003, username=admin3, password=123123, age=12)
User(id=004, username=admin2, password=123123, age=15)
User(id=005, username=admin4, password=321332, age=12)
User(id=009, username=admin5, password=123123, age=12)Process finished with exit code 0

使用Comparator接口可以不用再到实体类中指定比较规则,直接使用匿名内部类的方式指定排序规则,相较于Comparable接口更加灵活。

可这样的做法,只适用于偶尔一两次的排序,你能明白我的意思吗?如果在项目中大量地用到了排序,每次排序我们都要去写一个内部类指定排序规则,这样难免写出很多相同代码,使整个程序看起来臃肿,为了应对这样的场景,我们接着往下看。

六、因果不沾:Comparator接口——抽取多种排序规则

如果在我们的需求中,存在多种排序方式,可以直接在实体类中写内部类,通过内部类实现Comparator接口,指定多种比较规则。而在我们排序的时候,直接创建内部类对象即可。

在User实体类中写俩内部类:SortUserById 和 SortUserByAge,实现Comparator接口

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private String id;private String username;private String password;private int age;// 根据id排序class SortUserById implements Comparator<User> {@Overridepublic int compare(User o1, User o2) {return o1.getId().compareTo(o2.getId());}}// 根据年龄排序class SortUserByAge implements Comparator<User> {@Overridepublic int compare(User o1, User o2) {return o1.getAge() - o2.getAge();}}}

在测试类中,我们直接new内部类实现排序

public class Test {public static void main(String[] args) {List<User> userList = new ArrayList<>();//                     id    username  password  ageuserList.add(new User("009", "admin5", "123123", 12));userList.add(new User("005", "admin4", "321332", 12));userList.add(new User("003", "admin3", "123123", 12));userList.add(new User("004", "admin2", "123123", 15));userList.add(new User("002", "admin1", "123123", 16));//userList.sort(new User().new SortUserById());// 通过id排序userList.sort(new User().new SortUserByAge());// 通过年龄排序userList.forEach(user -> System.out.println(user));}}

输出结果:

1、通过id排序:

User(id=002, username=admin1, password=123123, age=16)
User(id=003, username=admin3, password=123123, age=12)
User(id=004, username=admin2, password=123123, age=15)
User(id=005, username=admin4, password=321332, age=12)
User(id=009, username=admin5, password=123123, age=12)Process finished with exit code 0

2、通过年龄排序:

User(id=009, username=admin5, password=123123, age=12)
User(id=005, username=admin4, password=321332, age=12)
User(id=003, username=admin3, password=123123, age=12)
User(id=004, username=admin2, password=123123, age=15)
User(id=002, username=admin1, password=123123, age=16)Process finished with exit code 0

这样一次指定多种排序方式,在需要的时候直接创建排序规则,比之前的方法更加灵活和便捷!

然后紧接着下一步,我们要解决多重排序,比如:先通过年龄排序,同年龄的情况下,再按照id排序。

七、万劫不灭:Comparator接口——多重排序

多重排序,我们只需要新建一个内部类:

// 根据年龄,再根据id排序
class SortUserByAgeThenId implements Comparator<User>{@Overridepublic int compare(User o1, User o2) {int result = o1.getAge() - o2.getAge();if(result == 0)   // 若age相等,则比较idresult = o1.getId().compareTo(o2.getId());return result;}
}

测试方法同上,我们再看测试结果!

User(id=003, username=admin3, password=123123, age=12)
User(id=005, username=admin4, password=321332, age=12)
User(id=009, username=admin5, password=123123, age=12)
User(id=004, username=admin2, password=123123, age=15)
User(id=002, username=admin1, password=123123, age=16)Process finished with exit code 0

现在是不是完美了!

八、无所不知,无所不能:仿写Comparator

既然知道了如何使用Comparator接口,自然要自己手写一把自定义Comparator才过瘾!

老规矩,先写接口:

@FunctionalInterface
public interface MyComparator<T> {int myCompare(T o1, T o2);
}

接着写实体类和排序规则:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private String id;private String username;private String password;private int age;// 根据id排序class SortUserById implements MyComparator<User> {@Overridepublic int myCompare(User o1, User o2) {return o1.getId().compareTo(o2.getId());}}// 根据年龄排序class SortUserByAge implements MyComparator<User> {@Overridepublic int myCompare(User o1, User o2) {return o1.getAge() - o2.getAge();}}// 根据年龄,再根据id排序class SortUserByAgeThenId implements MyComparator<User> {@Overridepublic int myCompare(User o1, User o2) {int result = o1.getAge() - o2.getAge();if (result == 0)result = o1.getId().compareTo(o2.getId());return result;}}}

最后是测试类:

public class Test {public static void main(String[] args) {List<User> userList = new ArrayList<>();//                     id    username  password  ageuserList.add(new User("009", "admin5", "123123", 12));userList.add(new User("005", "admin4", "321332", 12));userList.add(new User("003", "admin3", "123123", 12));userList.add(new User("004", "admin2", "123123", 15));userList.add(new User("002", "admin1", "123123", 16));mysort(userList, new User().new SortUserByAgeThenId());// 自定义排序userList.forEach(user -> System.out.println(user));}// 自定义排序public static <T> void mysort(List<T> users, MyComparator<T> comparator) {for (int i = 0; i < users.size(); i++) {for (int j = i; j < users.size(); j++) {T a = users.get(i);T b = users.get(j);if (comparator.myCompare(a, b) > 0)Collections.swap(users, i, j);}}}}

输出结果:

User(id=003, username=admin3, password=123123, age=12)
User(id=005, username=admin4, password=321332, age=12)
User(id=009, username=admin5, password=123123, age=12)
User(id=004, username=admin2, password=123123, age=15)
User(id=002, username=admin1, password=123123, age=16)Process finished with exit code 0

很简单有木有?接下来是优化的写法。

九、天道不灭,圣人不死:使用lambda表达式,简化Comparator写法

这里我们优化的是匿名内部类。

在上文中,我们有一段代码是这样写的:

// 排序
userList.sort(new Comparator<User>() {@Overridepublic int compare(User o1, User o2) {return o1.getId().compareTo(o2.getId());}
});

在Java 8中,我们可以使用函数式编程,Lambda表达式在Java语言中引入了一个操作符**“->”**,该操作符被称为Lambda操作符或箭头操作符。它将Lambda分为两个部分:

  • 左侧:指定了Lambda表达式需要的所有参数
  • 右侧:指定了Lambda体,即Lambda表达式要执行的功能。

于是乎,上面的方法,等同于下面的方法。

userList.sort((o1, o2) -> {return o1.getId().compareTo(o2.getId());
});

我们继续简化,去掉花括号:

userList.sort((o1, o2) -> o1.getId().compareTo(o2.getId()));

代码逐渐优雅起来,直接压缩成一句代码。

飞升——羽化登仙

掌握了上面的一系列比较方法,在应对不同场景的比较时,基本上可以做到游刃有余,立于不败之地!

学废没?反正我是学废了~~略略略

Java排序修真:从入门到进阶,从后天生灵到祖神祖仙相关推荐

  1. java入门学习_Java入门学习进阶知识点

    Java入门学习进阶知识点 入门阶段,主要是培养Java语言的编程思想.了解Java语言的语法,书写规范等,掌握Eclipse.MyEclipse等开发工具,编写Java代码的能力.学完这个阶段你应该 ...

  2. 服务端工程师入门与进阶 Java 版

    前言 欢迎加入我们.这是一份针对实习生/毕业生的服务端开发入门与进阶指南.遇到问题及时问你的 mentor 或者直接问我. 建议: 尽量用google查找技术资料. 有问题在stackoverflow ...

  3. Java学习之路整理-技术书从入门到进阶最全50+本(珍藏版 )

    学习交流加 个人微信:LyyCoder 学习交流资源分享qq群1(已满): 962535112 学习交流资源分享qq群2: 780902027 一.速读一遍(最好在1~2天内完成) 人的大脑记忆力有限 ...

  4. Java学习之路吐血整理技术书从入门到进阶最全50+本(珍藏版)

    本博文系转载:原博地址:https://blog.csdn.net/qq_43336600/article/details/83537307 重要:注:原博整理的资源是加密资源,所以我在进行转载时已经 ...

  5. python数据结构推荐书-「算法与数据结构」从入门到进阶吐血整理推荐书单

    推荐一下「算法与数据结构」从入门到进阶的书单. 一.入门系列 这些书籍通过图片.打比方等通俗易懂的方法来讲述,让你能达到懂一些基础算法,线性表,堆栈,队列,树,图,DP算法,背包问题等,不要求会实现, ...

  6. Lucene从入门到进阶(6.6.0版本)

    Lucene学习笔记 前言 基于最新的Lucene-6.6.0进行学习,很多方法都过时并不适用了,本文尽可能以最简单的方法入门学习. 第二章的例子都是官方的例子,写得很好很详细,但是竟然一句注释都没有 ...

  7. Python有多火,来看一份24小时榜单,从入门到进阶,赶紧收藏!

    现在有数百种编程语言可供使用,从成熟的C和C++到Ruby.C#和Lua等新秀,再到Java这样的企业级重器.要选择一门编程语言来学习确实很难.虽然没有一种语言能适合任何场景,但我觉得,对于大量的编程 ...

  8. Python有多火,来看一份24小时榜单,从入门到进阶

    早起看了一下榜单,霸榜第一屏的是Python,小编这里看第一屏前占名Python就占据4本. 现在有数百种编程语言可供使用,从成熟的C和C++到Ruby.C#和Lua等新秀,再到Java这样的企业级重 ...

  9. 尚硅谷Java零基础极速入门七天版笔记

    Java零基础极速入门 文章目录 Java零基础极速入门 1 Java快速入门 1.1计算机语言 1.2 Java语言 1.3 JVM 1.4 环境配置 2 基础语法 2.1 变量 2.2 标识符 2 ...

最新文章

  1. linux目录都是什么意思,linux中的基本的目录结构都是什么意思,初学者都应该有印象-tmp是什么文件...
  2. 百家争鸣|国内外NLP领域学术界和工业界的牛人和团队
  3. VMware虚拟机直连物理网络的两种方式
  4. 图的邻接矩阵存储和邻接表存储定义方法
  5. MapReduce Java API-使用Partitioner实现输出到多个文件
  6. python文件输出中文_【python】中文的输出,打印,文件编码问题解决方法
  7. php能不能动态显示html5,php – 是否可以动态生成html5缓存清单?
  8. 渗透测试中dns log的使用
  9. python读取excel写入mysql pandas_python pandas 读取文件 写入文件excel
  10. thinkphp开发的活动报名小程序源码带后台管理完整的报名小程序源码
  11. 谈薪资被 HR 怼了:估计你一辈子就是个程序员!气不过啊。。。
  12. ubuntu下使用命令行查看opencv版本
  13. java 错误声音播放器_JavaME 声音播放器的使用
  14. 一个小玩意 PHP实现微信红包金额拆分试玩
  15. 实习僧网站字体反爬破解思路及步骤分享
  16. 如果你还不懂区块链那就out了(三)--区块链3.0的优秀解决方案:Hyperledger fabric
  17. 花卉识别--五个类别的检测
  18. 加密市场熊市最后的曙光——Treasure Project(藏宝计划)
  19. Armijo条件,Wolfe条件,Goldstein条件
  20. Unity中场景生命周期的监听: EditorSceneManager

热门文章

  1. QQ 浏览器品牌升级,提出「搜刷用看」四项核心功能
  2. 计算机毕业设计(90)php小程序毕设作品之电影院售票小程序系统
  3. 网络安全之社工攻击的防护
  4. ios关闭摇动撤销代码_如何在iOS 9中禁用“摇动撤消”功能
  5. REST表述性状态传递
  6. excel剔除空格_excel怎样快速删除空格
  7. ERP中BOM的数据库设计与实现
  8. java高校学生电器报修系统ssm高校后勤报修系统小程序源码和论文
  9. 怎么自学Python?学习路线?
  10. Android 泽宇GC垃圾回收机制算法