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

原文出处: 陶邦仁

binarySearch()方法

二分法查找算法,算法思想:当数据量很大适宜采用该方法。采用二分法查找时,数据需是排好序的。 基本思想:假设数据是按升序排序的,对于给定值x,从序列的中间位置开始比较,如果当前位置值等于x,则查找成功;若x小于当前位置值,则在数列的前半段中查找;若x大于当前位置值则在数列的后半段中继续查找,直到找到为止。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

//针对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)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

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()方法

采取的是快速排序算法,算法思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

/**

   * 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);

  }

sort()方法

针对double,float类型数组排序,采取了先把所有的数组元素值为-0.0d的转换成0.0d,再利用快速排序排好序,最后再还原。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

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;

        }

    }

http://www.importnew.com/19952.html#comment-499513

时间: 2024-08-06 08:13:08

深入JDK源码之Arrays类中的排序查找算法(转)的相关文章

【ThreadLocal】深入JDK源码之ThreadLocal类

学习JDK中的类,首先看下JDK API对此类的描述,描述如下: 该类提供了线程局部 (thread-local) 变量.这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本.ThreadLocal其实就是一个工具类,用来操作线程局部变量,ThreadLocal 实例通常是类中的 private static 字段.它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联. 例如,以下类生成对每个线程

JDK源码之HashMap 类分析

一 概述 HashMap实现 hashmap继承了AbstractMap,实现了Map接口和Cloneable接口,HashMap是基于哈希表(散列表),实现Map接口的双列集合 jdk8中底层数据结构已经改为二叉树,之前是链表 看hashmap之前,需要把Map,AbstractMap源码撸一遍,这里放我的博文链接: https://www.cnblogs.com/houzheng/p/12687883.html 涉及到的数据结构 二 源码分析 属性 静态内部类(Entry的实现) 三 总结

jdk源码理解-String类

String类的理解 简记录一下对于jdk的学习,做一下记录,会持续补充,不断学习,加油 1.String的hash值的计算方法. hash值的计算方法多种多样,jdk中String的计算方法如下,比较简单,由字符串中的字符的ASCII值计算出来. /** * Returns a hash code for this string. The hash code for a * <code>String</code> object is computed as * <block

深入JDK源码之HashMap类

基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同.)此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能.迭代 collection 视图所需的时间与 HashMap 实例的"容量"(桶的数量)及其大小(键-值映射关系数)成比例.所以,如果迭代性

使用Eclipse跟踪JDK源码

首先我们要学会的是将JDK源码加载Eclipse中. 1.点"窗口"-->"首选项",选择左边的"Java"-->"已安装的JRE",然后选择我们安装的JRE,并单击它,然后选择右边的"编辑". 点"编辑"将出现如下的界面: 2.跟踪阅读源码 如上图,在我自己写的代码中包含了StringTokenizer类,我们要看它的具体定义,就只要按住"Ctrl"键,

JDK源码学习--String篇(二) 关于String采用final修饰的思考

JDK源码学习String篇中,有一处错误,String类用final[不能被改变的]修饰,而我却写成静态的,感谢CTO-淼淼的指正. 风一样的码农提出的String为何采用final的设计,阅读JDK源码的时候,有粗略的思考过,今天下班后又把<Thinking in Java>中关于final的内容重新看了一遍,对此写下一些关于自己的理解和想法. String类中final关键字的使用 final关键字,用来描述一块数据不能被改变,两种可能理由:设计.效率 final使用的三种情况:数据.方

调试JDK源码时可编辑操作的实现

目录 一.解压源码压缩包 二.创建Java项目 三.复制源码文件到文件夹 四.Platform Settings中新增一个SDK 五.修改新建SDK的 Sourcepath 配置 六.修改 Project 以及 Modules 的 SDK 七.项目结构图 八.测试调试过程中对源码进入注释 以下操作在以下环境中测试通过:idea 2019.3,jdk8源码包 一.解压源码压缩包 从jdk安装目录中找到 src.zip 的源码压缩包,解压后得到源码文件夹 二.创建Java项目 因主要目的是调试源码,

eclipse 如何debug jdk源码(转)

转:http://blog.csdn.net/cherrycheng_/article/details/51004386 原英文地址:http://stackoverflow.com/questions/18255474/debug-jdk-source-cant-watch-variable-what-it-is 问题效果图: 解决后的效果: 具体步骤: 1.制作可调试的rt.jar包 1.1 .在D盘新建jdk7_src及jdk7_rt-debug两个文件夹 1.2.复制JDK源码 将环境变

Java中集合框架,Collection接口、Set接口、List接口、Map接口,已经常用的它们的实现类,简单的JDK源码分析底层实现

(一)集合框架: Java语言的设计者对常用的数据结构和算法做了一些规范(接口)和实现(实现接口的类).所有抽象出来的数据结构和操作(算法)统称为集合框架. 程序员在具体应用的时候,不必考虑数据结构和算法实现细节,只需要用这些类创建一些对象,然后直接应用就可以了,这样就大大提高了编程效率. (二)集合框架包含的内容: (三)集合框架的接口(规范)   Collection接口:存储一组不唯一,无序的对象 List接口:存储一组不唯一,有序的对象 Set接口:存储一组唯一,无序的对象 Map接口: