jdk1.6与jdk1.7list集合排序区别与算法
源码分析:
在Collections.sort中:
|
|
public static <T extends Comparable<? super T>> void sort(List<T> list) {
|
可以发现,最终还是使用了Arrays.sort(a);的,不同的是:一个针对数组,一个针对集合
扩展:不同版本的内部实现问题
在JDK1.6以下的时候:调用sort方法时,默认是使用mergeSort的算法
而JDK1.7后,使用TimSort的算法。源码如下:JDK7的sort方法:
|
|
public static void sort(Object[] a) {
|
JDK6以下的sort方法:
|
|
public static void sort(Object[] a) {
|
当然可以使用下列的方式,在JDK7依旧使用mergeSort算法:
|
|
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
|
但是,从注释中可以发现,以后将让TimSort代替mergeSort。
根据这篇JDK7中的排序算法详解—Collections.sort和Arrays.sort找到了解决方法:
而在Java 7中,内部实现换成了TimSort,其对对象间比较的实现要求更加严格:
Comparator的实现必须保证以下几点(出自这儿)):
- sgn(compare(x, y)) == -sgn(compare(y, x))
- (compare(x, y)>0) && (compare(y, z)>0) 意味着 compare(x, z)>0
- compare(x, y)==0 意味着对于任意的z:sgn(compare(x, z))==sgn(compare(y, z)) 均成立
所以,compare要相应的改成:
|
|
public int compare(ComparatorTest o1, ComparatorTest o2) {
|
先对相等的情况判断,再对大小的判断。
算法分析
算法步骤
2.选择两个点P1,P2作为轴心,比如我们可以使用第一个元素和最后一个元素。
3.P1必须比P2要小,否则将这两个元素交换,现在将整个数组分为四部分:
(1)第一部分:比P1小的元素。
(2)第二部分:比P1大但是比P2小的元素。
(3)第三部分:比P2大的元素。
(4)第四部分:尚未比较的部分。
在开始比较前,除了轴点,其余元素几乎都在第四部分,直到比较完之后第四部分没有元素。
4.从第四部分选出一个元素a[K],与两个轴心比较,然后放到第一二三部分中的一个。
5.移动L,K,G指向。
6.重复 4 5 步,直到第四部分没有元素。
7.将P1与第一部分的最后一个元素交换。将P2与第三部分的第一个元素交换。
8.递归的将第一二三部分排序。
图表演示
算法源码
- //对外公开的两个sort方法
- public static void sort(int[] a) {
- sort(a, 0, a.length);
- }
- public static void sort(int[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- dualPivotQuicksort(a, fromIndex, toIndex - 1, 3);
- }
- //对数组的边界检测
- private static void rangeCheck(int length, int fromIndex, int toIndex) {
- if (fromIndex > toIndex) {
- throw new IllegalArgumentException("fromIndex > toIndex");
- }
- if (fromIndex < 0) {
- throw new ArrayIndexOutOfBoundsException(fromIndex);
- }
- if (toIndex > length) {
- throw new ArrayIndexOutOfBoundsException(toIndex);
- }
- }
- //交换数组中两个元素
- private static void swap(int[] a, int i, int j) {
- int temp = a[i];
- a[i] = a[j];
- a[j] = temp;
- }
- /**
- * 双轴快排的具体实现
- * @param a 待排序数组
- * @param left 数组需排序上界
- * @param right 数组需排序下界
- * @param div 理解为从何位置取轴
- */
- private static void dualPivotQuicksort(int[] a, int left,int right, int div) {
- int len = right - left;
- //数组长度如果很小(27),则直接用插入排序对其排序
- if (len < 27) {
- for (int i = left + 1; i <= right; i++) {
- for (int j = i; j > left && a[j] < a[j - 1]; j--) {
- swap(a, j, j - 1);
- }
- }
- return;
- }
- //取到位于1/div和div-1/div位置的点,并用他们来做轴
- int third = len / div;
- int m1 = left + third;
- int m2 = right - third;
- if (m1 <= left) {
- m1 = left + 1;
- }
- if (m2 >= right) {
- m2 = right - 1;
- }
- //确保left是小的,right是大的
- if (a[m1] < a[m2]) {
- swap(a, m1, left);
- swap(a, m2, right);
- }
- else {
- swap(a, m1, right);
- swap(a, m2, left);
- }
- // 两个轴
- int pivot1 = a[left];
- int pivot2 = a[right];
- // 代表比p1小和比p2大的两个指针
- int less = left + 1;
- int great = right - 1;
- // 开始取出less到great之间的未知大小数据,与两个轴比较
- // 并且将数据放入正确的区域后调整各个指针
- for (int k = less; k <= great; k++) {
- //如果取出的数比p1小,那么直接到less左侧,并且less右移
- if (a[k] < pivot1) {
- swap(a, k, less++);
- }
- //如果取出的数比p2大,那么首先确定great左侧没有比p2大的数
- //然后与great位置的数字交换,great左移
- //此时,great交换的数字肯定是比p2小或者相等的(首先确定过)
- //那么此时再与p1相比,处理这个数的区间
- else if (a[k] > pivot2) {
- while (k < great && a[great] > pivot2) {
- great--;
- }
- swap(a, k, great--);
- if (a[k] < pivot1) {
- swap(a, k, less++);
- }
- }
- //如果这个数比p1大但是比p2小,则不需要交换,只需将k指针右移
- }
- //将p1与less左侧的第一个数交换
- swap(a, less - 1, left);
- //将p2与great右侧的第一个数交换
- swap(a, great + 1, right);
- // 计算出在两轴大小之间的个数
- int dist = great - less;
- //如果这个数很小(13),那么取轴的点向两边偏
- if (dist < 13) {
- div++;
- }
- // 对三个子区间分别排序,因为less-1和great+1是轴,已经排好了序
- // 所以不需要比较
- dualPivotQuicksort(a, left, less - 2, div);
- dualPivotQuicksort(a, great + 2, right, div);
- // 如果在中间区间的数字很多,那么排除掉一些相等的元素再进行排序
- if (dist > len - 13 && pivot1 != pivot2) {
- for (int k = less; k <= great; k++) {
- if (a[k] == pivot1) {
- swap(a, k, less++);
- }
- else if (a[k] == pivot2) {
- swap(a, k, great--);
- if (a[k] == pivot1) {
- swap(a, k, less++);
- }
- }
- }
- }
- // 对中间的区间排序
- if (pivot1 < pivot2) {
- dualPivotQuicksort(a, less, great, div);
- }
- }
总结
jdk1.6与jdk1.7list集合排序区别与算法的更多相关文章
- 记一次诡异的bug调试——————关于JDK1.7和JDK1.8中HashSet的hash(key)算法的区别
现象: 测试提了一个bug,我完全复现不了,但是最吊诡的是在其他人的机器上都可以复现.起初以为是SVN合并后出现的冲突,后来经过对比法排查: step 1: 我本地开两个jetty,一个跑合并之前的版 ...
- 集合排序 Comparator和Comparable的使用区别
Java 排序 Compare Comparator接口 Comparable接口 区别 在Java中使用集合来存储数据时非常常见的,集合排序功能也是常用功能之一.下面看一下如何进行集合排序,常用的 ...
- Jdk1.7 与 jdk1.8的区别,最新的特征有哪些(美团,360,京东面试题目)
在jdk7的新特性方面主要有下面几方面的增强: 1.1二进制变量的表示,支持将整数类型用二进制来表示,用0b开头. 所有整数int.short.long.byte都可以用二进制表示: byte aBy ...
- java经常看见 jdk5 jdk1.5 —— jdk6 jdk1.6 这两者有什么区别吗?
问.java经常看见 jdk5 jdk1.5 —— jdk6 jdk1.6 这两者有什么区别吗? 答:没有区别,jdk5 和 jdk1.5 所代表的意思是一样的,只是叫法不一样 关键字: jdk5 j ...
- 集合排序Comparable和Comparator有什么区别?
Comparable和Comparator兄弟俩长得是真像.但是,需要注意下,使用中它们还是有不少区别的.下面,就一探究竟吧. 一.Comparator 做过集合排序的童鞋应该知道,可以使用Colle ...
- 牛客网Java刷题知识点之HashMap的实现原理、HashMap的存储结构、HashMap在JDK1.6、JDK1.7、JDK1.8之间的差异以及带来的性能影响
不多说,直接上干货! 福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号: 大数据躺过的坑 Java从入门到架构师 人工智能躺过的坑 ...
- CopyOnWriteArrayList集合排序异常问题
1.集合自定义排序实现 对List集合的自定义排序想必大家都知道要使用如下的方式,通过实现Comparator接口并实现compare方法来实现. /** * * @方法名 changeChain * ...
- hashMap在jdk1.7与jdk1.8中的原理及不同
在分析jdk1.7中HashMap的hash冲突时,不知大家是否有个疑问就是万一发生碰撞的节点非常多怎么版?如果说成百上千个节点在hash时发生碰撞,存储一个链表中,那么如果要查找其中一个节点,那就不 ...
- Java集合排序及java集合类详解--(Collection, List, Set, Map)
1 集合框架 1.1 集合框架概述 1.1.1 容器简介 到目前为止,我们已经学习了如何创建多个不同的对象,定义了这些对象以后,我们就可以利用它们来做一 ...
随机推荐
- PowerShell 总结
PowerShell 总结 1. 2. 3. 参考学习资料 (1). PowerShell 在线教程 (2). 利用Powershell在IIS上自动化部署网站 视频教程: (3). Windows ...
- HTTP协议详解之http请求分析
当今web程序的开发技术真是百家争鸣,ASP.NET, PHP, JSP,Perl, AJAX 等等. 无论Web技术在未来如何发展,理解Web程序之间通信的基本协议相当重要, 因为它让我们理解了We ...
- Spring boot 学习 五:domain的定义
一 public class City implements Serializable 实现了Sericalizable接口,只是一种标志.表示可以被序列化. java的ObjectOutputStr ...
- JavaScript高级程序设计学习笔记第四章--变量、作用域和内存问题
1.变量可能包含两种不同数据类型的值:基本类型值和引用类型值. 基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象. 2.变量复制 如果从一个变量向另一个变量复制基本类型的值,会在 ...
- ipv4 ipv6简介
互联网协议地址(英语:Internet Protocol Address,又译为网际协议地址),缩写为IP地址(IP Address),在Internet上,一种给主机编址的方式.常见的IP地址,分为 ...
- [Xcode 实际操作]六、媒体与动画-(15)使用AudioPlayer播放音乐
目录:[Swift]Xcode实际操作 本文将演示如何播放音频素材. 在项目名称上点击鼠标右键,弹出右键菜单, 选择[Add Files to "DemoApp"],往项目中导入文 ...
- Etherscan API 中文文档-智能合约
本文原文链接 点击这里获取Etherscan API 中文文档(完整版) 完整内容排版更好,推荐读者前往阅读. 智能合约(Contracts) 智能合约相关的 API,接口的参数说明请参考Ethers ...
- CSS揭秘—打字动效(四)
前言: 所有实例均来自<CSS揭秘>,该书以平时遇到的疑难杂症为引,提供解决方法,只能说秒极了,再一次刷新了我对CSS的认知 该书只提供了关键CSS代码,虽然有在线示例代码链接,但 ...
- 黑马学习JavaWeb入门总结
- p标签间距问题
用<p></p>标签写文本时,控制行与行之间的高度最好用line-height,不要用margin或padding: 因为P标签本身就带有一定的上下间距,且自带的间距在模拟 ...