Collections在sort()简单分析法源
- Collections的sort方法代码:
public static <T> void sort(List<T> list, Comparator<? super T> c) {
Object[] a = list.toArray();
Arrays.sort(a, (Comparator)c);
ListIterator i = list.listIterator();
for (int j=0; j<a.length; j++) {
i.next();
i.set(a[j]);
}
}
这种方法的定义:<T extends Comparable<? super T>> 表示该方法中传递的泛型參数必须实现了Comparable中的compareTo(T o)方法,否则进行不了sort排序。
把这种方法细分为3个步骤:
(1)将list装换成一个对象数组
(2)将这个对象数组传递给Arrays类的sort方法(也就是说collections的sort事实上本质是调用了Arrays.sort)
(3)完毕排序之后,再一个一个地。把Arrays的元素拷贝到List中。
- Collections方法实际是调用了Arrays的sort方法实现的,再看Arrays的sort方法代码:
public static void sort(Object[] a, int fromIndex, int toIndex) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, fromIndex, toIndex);
else
ComparableTimSort.sort(a, fromIndex, toIndex);
}
注意到1.7下的sort有一个分支推断。当LegacyMergeSort.userRequested为true的情况下,採用legacyMergeSort,否则採用ComparableTimSort。
LegacyMergeSort.userRequested的字面意思大概就是“用户请求传统归并排序”的意思。这个分支调用的是与jdk1.5同样的方法来实现功能。
ComparableTimSort是改进后的归并排序,对归并排序在已经反向排好序的输入时表现为O(n^2)的特点做了特别优化。对已经正向排好序的输入降低回溯。
对两种情况(一会升序。一会降序)的输入处理比較好(摘自百度百科)。
- legacyMergeSort代码:
private static void legacyMergeSort(Object[] a,
int fromIndex, int toIndex) {
rangeCheck(a.length, fromIndex, toIndex);
Object[] aux = copyOfRange(a, fromIndex, toIndex);
mergeSort(aux, a, fromIndex, toIndex, -fromIndex);
}
- mergeSort代码:
private static void mergeSort(Object[] src,
Object[] dest,
int low,
int high,
int off) {
int length = high - low; // Insertion sort on smallest arrays
if (length < INSERTIONSORT_THRESHOLD) {
for (int i=low; i<high; i++)
for (int j=i; j>low &&
((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
swap(dest, j, j-1);
return;
} // Recursively sort halves of dest into src
int destLow = low;
int destHigh = high;
low += off;
high += off;
int mid = (low + high) >>> 1;
mergeSort(dest, src, low, mid, -off);
mergeSort(dest, src, mid, high, -off); // If list is already sorted, just copy from src to dest. This is an
// optimization that results in faster sorts for nearly ordered lists.
if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {
System.arraycopy(src, low, dest, destLow, length);
return;
} // Merge sorted halves (now in src) into dest
for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)
dest[i] = src[p++];
else
dest[i] = src[q++];
}
}
①:这种方法接收Object[] src,Object[] dest两个数组,依据调用它的方法能够看出仅仅须要对dest[]这个数组中的元素进行排序后。传递过来的List<T> list也即排好了序。src[]数组用来进行中介的,也即后面的方法须要调用(所以这两个数组必须区分作用)。这里有个推断条件为length < INSERTIONSORT_THRESHOLD,INSERTIONSORT_THRESHOLD为Arrays的一个常量为7,它定义了假设数组元素小于7的话就直接用swap方法排序,提高了程序的运行效率。
②:当数组元素大于7的时候,程序先将数组拆分成低区间和高区间两个区间,再调用两个递归对这两个区间元素进行排序。在递归时还得推断已经划分的区间元素是否还多于7位,假设多于7位的话继续划分成两个区间,这样循环递归调用。在这种方法中,要特别注意src[]和dest[]的參数传递位置。调用递归方法时,是将src[]数组作为排序对象进行排序。src[]排序后,在通过③或④方法将dest[]数组根据src进行排序。终于达到List<T> list排序的结果。
③:假设初始元素个数大于等于7个的话(小于7的直接在①方法排好序返回)进过②方法后。仅仅有两种情况:两个排好序的低区间和高区间。这种方法作用是:假设低区间列表中的最高元素小于高区间列表中的最低元素,则表明该次递归循环的区间段已经排好序,然后将这段数据拷贝到dest[]数组中。
反之则进入方法④。
④:进入该方法表明该次递归循环的低区间的数字最高元素大于高区间列表中的最低元素,也就是说低区间的数组元素值都大于高区间的数组元素值。因此将src中的高区间元素和低区间元素调换放入dest数组中。这样一次递归循环就调用完毕,假设还有循环就继续排序下去,否则排序就已经完毕。
- ComparableTimSort.sort(a)代码:(关于此方法的解说)
static void sort(Object[] a, int lo, int hi) {
rangeCheck(a.length, lo, hi);
int nRemaining = hi - lo;
if (nRemaining < 2)
return; // Arrays of size 0 and 1 are always sorted
// If array is small, do a "mini-TimSort" with no merges
if (nRemaining < MIN_MERGE) {
int initRunLen = countRunAndMakeAscending(a, lo, hi);
binarySort(a, lo, hi, lo + initRunLen);
return;
}
/**
* March over the array once, left to right, finding natural runs,
* extending short natural runs to minRun elements, and merging runs
* to maintain stack invariant.
*/
ComparableTimSort ts = new ComparableTimSort(a);
int minRun = minRunLength(nRemaining);
do {
// Identify next run
int runLen = countRunAndMakeAscending(a, lo, hi);
// If run is short, extend to min(minRun, nRemaining)
if (runLen < minRun) {
int force = nRemaining <= minRun ? nRemaining : minRun;
binarySort(a, lo, lo + force, lo + runLen);
runLen = force;
}
// Push run onto pending-run stack, and maybe merge
ts.pushRun(lo, runLen);
ts.mergeCollapse();
// Advance to find next run
lo += runLen;
nRemaining -= runLen;
} while (nRemaining != 0);
// Merge all remaining runs to complete sort
assert lo == hi;
ts.mergeForceCollapse();
assert ts.stackSize == 1;
}
參考:
http://zoujialiang.iteye.com/blog/866902
http://blog.csdn.net/pickless/article/details/9297895
http://jxlanxin.iteye.com/blog/1814164
版权声明:本文博客原创文章,博客,未经同意,不得转载。
Collections在sort()简单分析法源的更多相关文章
- 简单分析 ztree 源码
为了把 SVG标注 代码抽成一个库,我要学习一下 ztree 是怎么写的. 开始正文. 这只是一个很简单的版本,以后可能会详细分析... (function ($) { var settings = ...
- FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分
===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...
- CardboardSDK-iOS 源码简单分析
该项目地址: 地址 克隆地址为 https://github.com/rsanchezsaez/CardboardSDK-iOS.git 目前如果想在iOS设备上实现双目VR的功能,Google 已经 ...
- FFmpeg源码简单分析:libswscale的sws_scale()
===================================================== FFmpeg的库函数源码分析文章列表: [架构图] FFmpeg源码结构图 - 解码 FFm ...
- Django-session中间件源码简单分析
Django-session中间件源码简单分析 settings里有关中间件的配置 MIDDLEWARE = [ 'django.middleware.security.SecurityMiddlew ...
- FFmpeg源码简单分析:结构体成员管理系统-AVOption
===================================================== FFmpeg的库函数源码分析文章列表: [架构图] FFmpeg源码结构图 - 解码 FFm ...
- negroni-gzip源码简单分析解读
negroni-gzip源码简单分析解读 这是一个为Negroni设计的gzip压缩处理中间件,需要用到已有的compress中的gzip,阅读了不长的源码之后,总结了一些关键要点和注意点. 检查是否 ...
- FFmpeg的HEVC解码器源码简单分析:概述
===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...
- FFmpeg的HEVC解码器源码简单分析:解码器主干部分
===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...
随机推荐
- Laravel 简单使用七牛云服务
前言 路漫漫其修远兮,吾将上下而求索.学习 Laravel 之初觉得所有东西都很厉害的样子,现在看来就是很厉害啊!最近在写一个项目上传的模块,要上传图片到七牛云,昨天看了一下午七牛云官方的文档感觉还是 ...
- 进度记录 和 安装imagick时Cannot locate header file MagickWand.h错误的解决
修改php.ini文件,已使php支持扩展的功能 [root@localhost imagick-2.2.2]# ./configure --with-php-config=/usr/local/ph ...
- Sed简介 (转)
Sed简介 sed 是一种在线编辑器,它一次处理一行内容.处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓 ...
- C语言循环小技巧
写代码,有两类追求,一种是追求实用(Coder),一种是追求代码艺术(Artist) 我是那种追实用追腻了,偶然追一下艺术(就是偶然和艺术有一腿)的那种Coder 很多人,已经习惯了for(i=0; ...
- (IOS)Apple 证书相关
1.私钥 本地钥匙串程序创建<证书请求文件>(.certSigningRequest),用其向苹果申请下载<证书文件>/<私钥>(.cer),并安装到钥匙串: 团队 ...
- springMVC框架下JQuery传递并解析Json数据
springMVC框架下JQuery传递并解析Json数据
- 告别IE给我们的web开发带来的困扰(使用chrome frame v8引擎)
茶爸爸个人微信:benyzhous,公众号:cha-baba欢迎骚扰 由于客户所有机器必须使用IE6浏览器,导致我们在开发项目过程中遇到非常多的样式与性能问题,在偶然的一次使用360软件管家搜索chr ...
- Ubuntu15.04上为火狐浏览器安装Adobe Flash Player插件
前言:最新版的ubuntu好像没有flashplayer,而且更新源也无法更新成功,找些资料终于发现 这个需要自己手动配置.由于flashplayer无法安装,导致视频,百度上传等功能都无法使用: 安 ...
- java 如何自定义异常 用代码展示 真心靠谱
先建两个自定义的异常类 ChushufuException类 class ChushufuException extends Exception { public ChushufuException( ...
- linux 下搭建 ftp
最近为了方便目标板与PC机上LINUX之间的通讯,就在LINUX搭建了FTP,工作不难,写个总结.主要经过以下几个步骤: 1.检查是否安装了vsftpd服务器 可以用rpm -q vsftpd命令来查 ...