【问题标题】:Sort an array of primitives with a custom comparator and without converting to objects使用自定义比较器对基元数组进行排序,无需转换为对象
【发布时间】:2014-03-26 15:51:00
【问题描述】:

在 Java 中使用自定义比较器(或键)函数对原始数组进行排序的最简单方法是什么,并且无需转换为对象数组(为了提高性能†)。

†(只是预防措施,我不是在问不转换为对象是否是性能 POV 的一个好的决定。)

【问题讨论】:

  • 尝试实现快速排序(或者只是从网络上获取任何实现)。在侧节点上,Collections.sort() 使用合并排序。
  • 相信(为什么这不是一个答案)您可以使用 Object 表示的原语和 Arrays.sort() 方法...例如,您可以将Long 用于long[]。还没有尝试过,但我可能是错的......我不确定一个基元数组是否可以透明地转换为相应对象的数组。
  • Collections.sort() 曾经使用归并排序,但现在它使用“TimSort”,尽管如果您想使用旧版归并排序,可以调用 API 中的一些内容。
  • fhucho 你问是否有图书馆的方式来做到这一点?如果是这样,那就没有了。比较器接口将需要转换为对象。 darijan 的回答是从我这里得到 +1。 “最简单的方法”就是自己实现一个好的排序算法,比较自己想要的方式。
  • 这不是完全重复,但这里的讨论可能会有所帮助:stackoverflow.com/questions/215271/…

标签: java


【解决方案1】:

标准 Java 库不支持使用自定义比较器对原始值数组进行排序

您可以轻松地从头开始实现简单的排序(例如,bubblesort - O(N^2)),但问题是对于足够大的数组,您通过不转换为盒装类型而节省的成本会在效率较低的排序算法中丢失。

所以你的选择是:

  • 从头开始实现高性能排序(合并排序、修改后的快速排序等)。

  • 为不支持比较器的基本类型找到一个现有的高性能排序,并对其进行修改。

  • 看看你是否能找到一个合适的第三方库,它支持 ptimitive 数组和比较器。 (我还没找到……)

(注意:Comparator 接口在这里不起作用。它不适合比较原始类型。)

【讨论】:

    【解决方案2】:

    在 Java 8 中,您可以让排序方法采用函数接口。这是来自 OpenJDK 的修改代码(版权所有 1997-2007 Sun Microsystems, Inc. GPLv2):

    import java.util.function.LongBinaryOperator;
    
    public class ArraySort {
        public static void sort(long[] x, LongBinaryOperator op) {
            sort1(x, 0, x.length, op);
        }
    
        private static void sort1(long x[], int off, int len, LongBinaryOperator op) {
            if (len < 7) {
                for (int i=off; i<len+off; i++)
                    // Use custom comparator for insertion sort fallback
                    for (int j=i; j>off && (op.applyAsLong(x[j-1], x[j]) > 0); j--)
                        swap(x, j, j-1);
                return;
            }
    
            int m = off + (len >> 1);
            if (len > 7) {
                int l = off;
                int n = off + len - 1;
                if (len > 40) {
                    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);
            }
            long v = x[m];
    
            int a = off, b = a, c = off + len - 1, d = c;
            while(true) {
                // Use custom comparator for checking elements
                while (b <= c && (op.applyAsLong(x[b], v) <= 0)) {
                    if (x[b] == v)
                        swap(x, a++, b);
                    b++;
                }
                // Use custom comparator for checking elements
                while (c >= b && (op.applyAsLong(x[c], v) >= 0)) {
                    if (x[c] == v)
                        swap(x, c, d--);
                    c--;
                }
                if (b > c)
                    break;
                swap(x, b++, c--);
            }
    
            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);
    
            if ((s = b-a) > 1)
                sort1(x, off, s, op);
            if ((s = d-c) > 1)
                sort1(x, n-s, s, op);
        }
    
        private static void swap(long x[], int a, int b) {
            long t = x[a];
            x[a] = x[b];
            x[b] = t;
        }
    
        private static void vecswap(long x[], int a, int b, int n) {
            for (int i=0; i<n; i++, a++, b++)
                swap(x, a, b);
        }
    
        private static int med3(long 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));
        }
    }
    

    并使用 lambdas 或其他任何实现 LongBinaryOperator 接口的东西来调用它:

    import java.util.Arrays;
    
    public class Main {
        public static void main(String[] args) {
            long x[] = {5, 5, 7, 1, 2, 5, 8, 9, 23, 5, 32, 45, 76};
            ArraySort.sort(x, (a, b) -> b - a);         // sort descending
            System.out.println(Arrays.toString(x));
        }
    }
    

    输出:

    [76, 45, 32, 23, 9, 8, 7, 5, 5, 5, 5, 2, 1]
    

    【讨论】:

    • 请注意,由于上述内容是从 Sun / Oracle GPLv2 代码库复制(经过修改),如果>您
    【解决方案3】:

    您可以构建自己的比较函数, 并使用其中一种排序算法

    最简单和最慢:BubbleSort ( O(N^2) )。

    最难但最快:MergeSort( (O(Nlog(n) ) .

    现在在两种算法中,您都有询问 A > B 的部分,在这部分中您应该放置比较函数。

    boolean compare(int x, int y){
         if(/* your crazy compare condition */)
             return true;
         else return false; 
    }
    

    冒泡排序示例:

        procedure bubbleSort( A : list of sortable items )
       repeat     
         swapped = false
         for i = 1 to length(A) - 1 inclusive do:
           /* if this pair is out of order */
           if compare(A[i],A[i-1]) then // Notcie the compare instead of A[i] > A[i-1]
             /* swap them and remember something changed */
             swap( A[i-1], A[i] )
             swapped = true
           end if
         end for
       until not swapped
    end procedure
    

    希望对你有帮助

    【讨论】:

      【解决方案4】:

      创建一个表示数组中索引的整数列表,对列表进行排序。您可以为多种排序多次重复使用索引列表。

      【讨论】:

      • 这个主意不错,我可能会用到。
      • 这比装箱原语更好吗?
      【解决方案5】:

      我从 Java 6 的 Arrays.java 中复制了以下内容,并根据我的需要对其进行了修改。它在较小的数组上使用插入排序,因此它应该比纯快速排序更快。

      /**
       * 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);
      }
      
      /**
       * 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;
      }
      
      /**
       * Swaps x[a .. (a+n-1)] with x[b .. (b+n-1)].
       */
      private static void vecswap(int x[], int a, int b, int n) {
          for (int i=0; i<n; i++, a++, b++)
              swap(x, a, b);
      }
      
      /**
       * Returns the index of the median of the three indexed integers.
       */
      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));
      }
      

      【讨论】:

      • 请注意,由于上述内容是从 Sun / Oracle GPLv2 代码库复制(经过修改),如果>您
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-02
      • 2011-04-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多