使用自定义比较器对基元数组进行排序,而不转换为对象

时间:2014-03-26 15:51:01

标签: java

使用自定义比较器(或键)函数和对Java中的基本数组进行排序的最简单方法是什么,而不转换为对象数组(性能†)。

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

5 个答案:

答案 0 :(得分:2)

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

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

所以你的选择是:

  • 从头开始实施高性能排序(mergesort,modified quicksort等)。

  • 查找不支持比较器的原始类型的现有高性能排序,并对其进行修改。

  • 查看是否可以找到支持ptimitive数组和比较器的合适的第三方库。 (我还没设法找到一个......)

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

答案 1 :(得分: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));
    }
}

用lambda或其他任何实现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]

答案 2 :(得分:1)

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

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

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

现在在两个算法中你都有一个部分,询问是否A&gt; 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

希望这会有所帮助

答案 3 :(得分:0)

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

答案 4 :(得分:0)

我从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));
}