`

深入JDK源代码之Arrays类中的排序查找算法

 
阅读更多
    最近在暑假实习,没什么任务给我做,不是我不能做,而是还没那资格,毕竟才来了一周多。闲着无事,在网上看看国内的牛公司的招聘要求,想自己能达到他们的要求,准备研究下JDK中的常用类的源代码。今天就来看看java.util.Arrays类。这个类是个数组工具类。主要提供方法sort(),fill(),binarySearch(),还有数组复制等方法。打开源文件,刚超过4千行,不过包括很多注释,那么我在这里主要讲讲这里面涉及的排序算法和查找算法。
  一、binarySearch()方法,二分法查找算法,算法思想:当数据量很大适宜采用该方法。采用二分法查找时,数据需是排好序的。 基本思想:假设数据是按升序排序的,对于给定值x,从序列的中间位置开始比较,如果当前位置值等于x,则查找成功;若x小于当前位置值,则在数列的前半段中查找;若x大于当前位置值则在数列的后半段中继续查找,直到找到为止。
 
//针对int类型数组的二分法查找,key为要查找数的下标
	private static int binarySearch0(int[] a, int fromIndex, int toIndex,
				     int key) {
	int low = fromIndex;
	int high = toIndex - 1;
	while (low <= high) {
	    int mid = (low + high) >>> 1;//无符号左移一位,相当于除以二
	    int midVal = a[mid];

	    if (midVal < key)
		low = mid + 1;
	    else if (midVal > key)
		high = mid - 1;
	    else
		return mid; // key found
	}
	return -(low + 1);  // key not found.
    }

二、sort()方法针对引用类型数组采取的算法是归并排序。算法思想:归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
 
 private static final int INSERTIONSORT_THRESHOLD = 7;//插入排序门槛
   public static void sort(Object[] a) {
        Object[] aux = (Object[])a.clone();
        mergeSort(aux, a, 0, a.length, 0);
    }
    //归并排序
    private static void mergeSort(Object[] src,
				  Object[] dest,
				  int low,
				  int high,
				  int off) {
	       int length = high - low;

        if (length < INSERTIONSORT_THRESHOLD) { //若数组长度小于7,则用冒泡排序
            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++];
        }
    }

三、sort()方法采取的是快速排序算法,算法思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
  
 /**
     * Swaps x[a] with x[b].
     */
 private static void swap(int x[], int a, int b) {
	int t = x[a];
	x[a] = x[b];
	x[b] = t;
    }
 public static void sort(int[] a) {
	sort1(a, 0, a.length);
    }

 private static int med3(int x[], int a, int b, int c) {//找出三个中的中间值
	return (x[a] < x[b] ?
		(x[b] < x[c] ? b : x[a] < x[c] ? c : a) :
		(x[b] > x[c] ? b : x[a] > x[c] ? c : a));
    }

	  /**
     * Sorts the specified sub-array of integers into ascending order.
     */
  private static void sort1(int x[], int off, int len) {
	// Insertion sort on smallest arrays
	if (len < 7) {//采用冒泡排序
	    for (int i=off; i<len+off; i++)
		for (int j=i; j>off && x[j-1]>x[j]; j--)
		    swap(x, j, j-1);
	    return;
	}
    //采用快速排序
	// Choose a partition element, v
	int m = off + (len >> 1);       // Small arrays, middle element
	if (len > 7) {
	    int l = off;
	    int n = off + len - 1;
	    if (len > 40) {        // Big arrays, pseudomedian of 9
		int s = len/8;
		l = med3(x, l,     l+s, l+2*s);
		m = med3(x, m-s,   m,   m+s);
		n = med3(x, n-2*s, n-s, n);
	    }
	    m = med3(x, l, m, n); // Mid-size, med of 3
	}
	int v = x[m];

	// Establish Invariant: v* (<v)* (>v)* v*
	int a = off, b = a, c = off + len - 1, d = c;
	while(true) {
	    while (b <= c && x[b] <= v) {
		if (x[b] == v)
		    swap(x, a++, b);
		b++;
	    }
	    while (c >= b && x[c] >= v) {
		if (x[c] == v)
		    swap(x, c, d--);
		c--;
	    }
	    if (b > c)
		break;
	    swap(x, b++, c--);
	}

	// Swap partition elements back to middle
	int s, n = off + len;
	s = Math.min(a-off, b-a  );  vecswap(x, off, b-s, s);
	s = Math.min(d-c,   n-d-1);  vecswap(x, b,   n-s, s);

	// Recursively sort non-partition-elements
	if ((s = b-a) > 1)
	    sort1(x, off, s);
	if ((s = d-c) > 1)
	    sort1(x, n-s, s);
    }

四、针对double,float类型数组排序的sort()方法,采取了先把所有的数组元素值为-0.0d的转换成0.0d,再利用快速排序排好序,最后再还原。
  
public static long doubleToRawLongBits(double value)根据 IEEE 754 浮点“双精度格式”位布局,返回指定浮点值的表示形式,并保留 NaN 值。 
第 63 位(掩码 0x8000000000000000L 选定的位)表示浮点数的符号。第 62-52 位(掩码 0x7ff0000000000000L 选定的位)表示指数。第 51-0 位(掩码 0x000fffffffffffffL 选定的位)表示浮点数的有效数字(有时也称为尾数)。 

如果参数是正无穷大,则结果为 0x7ff0000000000000L。 

如果参数是负无穷大,则结果为 0xfff0000000000000L。 

如果参数是 NaN,则结果是表示实际 NaN 值的 long 整数。与 doubleToLongBits 方法不同,doubleToRawLongBits 并没有压缩那些将 NaN 编码为一个“规范的”NaN 值的所有位模式。 

在所有情况下,结果都是一个 long 整数,将其赋予 longBitsToDouble(long) 方法将生成一个与 doubleToRawLongBits 的参数相同的浮点值。 


参数:
value - 双精度 (double) 浮点数。 

下面是源代码中的方法:
   public static void sort(double[] a) {
	      sort2(a, 0, a.length);
      }
	  private static void sort2(double a[], int fromIndex, int toIndex) {
		  //static long doubleToLongBits(double value) 
		 //根据 IEEE 754 浮点双精度格式 ("double format") 位布局,返回指定浮点值的表示形式。
        final long NEG_ZERO_BITS = Double.doubleToLongBits(-0.0d);
        /*
         * The sort is done in three phases to avoid the expense of using
         * NaN and -0.0 aware comparisons during the main sort.
         */

        /*
         * Preprocessing phase:  Move any NaN's to end of array, count the
         * number of -0.0's, and turn them into 0.0's.
         */
        int numNegZeros = 0;
        int i = fromIndex, n = toIndex;
        while(i < n) {
            if (a[i] != a[i]) {  //这段搞不懂,源代码怪怪的,感觉多此一举
		double swap = a[i];
                a[i] = a[--n];
                a[n] = swap;
            } else {
                if (a[i]==0 && Double.doubleToLongBits(a[i])==NEG_ZERO_BITS) {
                    a[i] = 0.0d;
                    numNegZeros++;
                }
                i++;
            }
        }

        // Main sort phase: quicksort everything but the NaN's
	    sort1(a, fromIndex, n-fromIndex);

        // Postprocessing phase: change 0.0's to -0.0's as required
        if (numNegZeros != 0) {
            int j = binarySearch0(a, fromIndex, n, 0.0d); // posn of ANY zero
            do {
                j--;
            } while (j>=0 && a[j]==0.0d);

            // j is now one less than the index of the FIRST zero
            for (int k=0; k<numNegZeros; k++)
                a[++j] = -0.0d;
        }
    }
1
1
分享到:
评论

相关推荐

    jdk1.7.0_80_x86_32.zip

    - **多路归并排序**:JDK7中的`Arrays.sort()`和`Collections.sort()`方法使用了新的多路归并排序算法,提高了排序性能。 - **Fork/Join框架**:用于并行执行任务,提高计算密集型任务的执行效率。 - **元空间**...

    jdk 1.8.chm

    本篇将围绕"jdk 1.8 api"这一主题,深入探讨Java SDK 1.8中的关键知识点,帮助开发者提升效率,实现更高效、更优雅的代码编写。 1. **Lambda表达式** JDK 1.8引入了Lambda表达式,这是对函数式编程的一大迈进。...

    jdk-7u80-windows-x64.exe

    【标签】"源码软件" 暗示JDK包含的不仅有二进制文件,还可能包括Java语言的源代码,这对于学习和理解Java的内部工作原理非常有帮助。"windows" 表明这是为Windows操作系统设计的版本。"jdk-7" 指的是Java SE(标准版...

    JDK11_DSA_SrcComment:在JDK 11中阅读数据结构和算法(DSA)的注意事项

    "JDK11_DSA_SrcComment"可能是指一个项目或者资源,它专注于分析和解释JDK 11源代码中的数据结构和算法。这个项目可能是为了帮助开发者更好地理解JDK 11中实现的各种内部机制,从而提升编程技能和效率。 JDK(Java ...

    txtSort.zip

    在这个"txtSort"的压缩包中,很可能包含了一个或多个Java源代码文件,用于实现文本文件的排序功能。这些文件可能演示了如何读取文本文件,处理其中的数据(比如字符串或数字),然后进行排序,并可能将排序后的结果...

    JDK 1.5 中文文档.rar

    7. **注解**:提供了一种元数据机制,可以在源代码中添加元信息,用于编译时或运行时的处理,如`@Override`、`@Deprecated`等。 四、反射增强 8. **`java.lang.reflect.ParameterizedType`**:增加了对泛型类型的...

    java 相关问题(二)

    以下是一些关于Java中类排序及其相关的深入知识点: 1. **Java类排序**: - `Comparable`接口:Java中的每个类都可以实现`Comparable`接口,定义自己的比较逻辑。通过实现`compareTo()`方法,可以定义对象之间的...

    JDK1.8版本

    - **javac**:Java编译器,将源代码编译成字节码。 - **jar**:打包工具,可以创建、更新和提取.jar文件。 - **javadoc**:生成API文档的工具。 - **jconsole**:Java监控和管理控制台,用于监控应用程序...

    jdk-7windows-x64.rar

    1. **Java编译器 (javac)**:负责将源代码(.java文件)编译成字节码(.class文件),这是Java程序执行的第一步。 2. **Java虚拟机 (JVM)**:JVM是Java平台的核心,它负责运行编译后的字节码。JDK 7中的JVM在性能和...

    jdk1.7 64位免安装

    3. **多路归并排序**:`Arrays.sort()`方法在Java 7中使用了多路归并排序算法,提高了数组排序的性能。 4. **尝试-with资源**(Try-with-resources):这个新语法结构使得资源管理更加简洁和安全,确保了在finally...

    jdk1.7 7u80 64位

    4. **多路归并排序**:Java 7引入了一个新的并发排序算法,提高了`Arrays.sort()`和`Collections.sort()`的性能。 5. **新文件系统API (NIO.2)**:提供了更高级别的文件操作,如路径、文件属性和异步I/O。 6. **...

    JDK-API-CODE:jdk7 API的源代码,加上了自己的注释

    - **多路归并排序**:在`java.util`包中,JDK7引入了`java.util.Arrays.sort()`方法的改进版,采用了多路归并排序算法,提高了大规模数据排序的性能。 - **try-with-resources**:在`java.lang`包中,新增了`try-...

    java1.8源码-java_jdk1.8:jdk1.8源代码

    Java 1.8 源码是 Java 开发者深入理解 JDK 内部工作原理的重要资源,它揭示了 Java 核心库的实现细节。在 JDK 1.8 版本中,引入了许多重要的更新和优化,包括 Lambda 表达式、Stream API、Date 和 Time API 的改进...

    jdk8新特性详细介绍

    3. **方法引用**:除了Lambda表达式,JDK8还提供了方法引用,它可以直接引用类中的静态方法或者对象实例上的非静态方法,进一步简化了代码。例如,`Arrays.sort(list, Comparator.comparing(Person::getName))`。 4...

    jdk-7u80-windows-x64.zip

    1. **Java编译器 (javac)**:这是JDK的核心组件之一,用于将源代码编译成可执行的Java字节码。在JDK 1.7中,编译器支持新的语法特性,如钻石操作符()和多线程注解(@SafeVarargs)。 2. **Java运行时环境 (JRE)**...

    java程序设计实验报告.pdf

    安装和配置JDK是进行Java编程的第一步,常用命令如`javac`用于编译Java源代码,`java`用于运行已编译的类。 2. **数据类型**:Java有两大类数据类型:基本数据类型(如字节byte、短整型short、整型int、长整型long...

    面试java高级技术总结.pdf

    调试是识别和修复代码错误的过程,Java开发工具(JDK)内置的Java Debugger(JDB)可以帮助进行源代码级别的调试。 4. **集合框架**:集合是Java中存储多个对象的容器,包括接口(如`List`, `Set`, `Map`)和实现...

    java深度历险.pdf

    在某些特定情况下,如算法竞赛的在线评测系统,需要在运行时动态编译Java源代码。JSR 199引入了Java编译器API,允许在JDK 6及更高版本中动态编译Java代码。以下是一个简单的示例,展示了如何使用该API编译"Hello ...

    Java基础资料+重点面试题.pdf

    排序数组时,既可以使用手动实现的排序算法(如冒泡排序),也可以使用Java的Arrays类提供的sort方法进行自动排序。数组的相关知识点也是面试中的常客,包括如何创建和使用数组,以及数组排序和复制的方法。 九、...

Global site tag (gtag.js) - Google Analytics