我需要使用自定义比较器对整数数组进行排序,但是Java的库没有为具有比较器的整数提供排序功能(比较器只能与对象一起使用)。有没有简单的方法可以做到这一点?
Answers:
如果您无法更改输入数组的类型,则可以执行以下操作:
final int[] data = new int[] { 5, 4, 2, 1, 3 };
final Integer[] sorted = ArrayUtils.toObject(data);
Arrays.sort(sorted, new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
// Intentional: Reverse order for this demo
return o2.compareTo(o1);
}
});
System.arraycopy(ArrayUtils.toPrimitive(sorted), 0, data, 0, sorted.length);
这可以使用ArrayUtils
commons-lang项目轻松地在int[]
和之间进行转换Integer[]
,创建数组的副本,进行排序,然后将排序后的数据复制到原始副本上。
return o2.compareTo(o1);
这个对吗?我相信这样的顺序将像我们期望的那样逆转...
int
如何使用流(Java 8)?
int[] ia = {99, 11, 7, 21, 4, 2};
ia = Arrays.stream(ia).
boxed().
sorted((a, b) -> b.compareTo(a)). // sort descending
mapToInt(i -> i).
toArray();
或就地:
int[] ia = {99, 11, 7, 21, 4, 2};
System.arraycopy(
Arrays.stream(ia).
boxed().
sorted((a, b) -> b.compareTo(a)). // sort descending
mapToInt(i -> i).
toArray(),
0,
ia,
0,
ia.length
);
(a, b) -> b - a
用于倒序。该比较器可能溢出。注意Comparator.reverseOrder()
……的存在
如果您不想复制数组(例如,数组很大),则可能要创建一个List<Integer>
可以用于排序的包装器:
final int[] elements = {1, 2, 3, 4};
List<Integer> wrapper = new AbstractList<Integer>() {
@Override
public Integer get(int index) {
return elements[index];
}
@Override
public int size() {
return elements.length;
}
@Override
public Integer set(int index, Integer element) {
int v = elements[index];
elements[index] = element;
return v;
}
};
现在,您可以使用自定义比较器对此包装器列表进行排序。
sort
实现将整个列表复制到一个数组中,对其进行排序并写回。并且由于此列表未实现RandomAccess
标记,因此写回将使用aListIterator
而不是仅调用set
。
LinkedList
,直接排序将非常糟糕,因此他们会进行复制。他们为什么不检查的RandomAccess
原因尚不清楚,我想几乎没人知道这个标记界面。
RandomAccess
如果在将来的某个时候执行此优化,则扩展不会受到损害。但是,当前该方法无法实现其设置。
您可以IntArrays.quickSort(array, comparator)
从fastutil库中使用。
通过将您的int数组转换为Integer数组,然后使用public static <T> void Arrays.sort(T[] a,
Comparator<? super T> c)
(第一步,仅当我担心自动装箱可能会破坏数组工作时才需要这样做)。
这是完成工作的辅助方法。
首先,您将需要一个新的Comparator接口,因为Comparator不支持原语:
public interface IntComparator{
public int compare(int a, int b);
}
(您当然可以通过自动装箱/拆箱来做到这一点,但我不会去那里,这很丑)
然后,这是一个使用此比较器对int数组进行排序的辅助方法:
public static void sort(final int[] data, final IntComparator comparator){
for(int i = 0; i < data.length + 0; i++){
for(int j = i; j > 0
&& comparator.compare(data[j - 1], data[j]) > 0; j--){
final int b = j - 1;
final int t = data[j];
data[j] = data[b];
data[b] = t;
}
}
}
这是一些客户端代码。一个愚蠢的比较器,它对仅由数字“ 9”组成的所有数字排在最前面(再次按大小排序),然后对其余所有数字进行排序(无论有什么好处):
final int[] data =
{ 4343, 544, 433, 99, 44934343, 9999, 32, 999, 9, 292, 65 };
sort(data, new IntComparator(){
@Override
public int compare(final int a, final int b){
final boolean onlyNinesA = this.onlyNines(a);
final boolean onlyNinesB = this.onlyNines(b);
if(onlyNinesA && !onlyNinesB){
return -1;
}
if(onlyNinesB && !onlyNinesA){
return 1;
}
return Integer.valueOf(a).compareTo(Integer.valueOf(b));
}
private boolean onlyNines(final int candidate){
final String str = String.valueOf(candidate);
boolean nines = true;
for(int i = 0; i < str.length(); i++){
if(!(str.charAt(i) == '9')){
nines = false;
break;
}
}
return nines;
}
});
System.out.println(Arrays.toString(data));
输出:
[9, 99, 999, 9999, 32, 65, 292, 433, 544, 4343, 44934343]
排序代码取自Arrays.sort(int []),而我仅使用针对小型数组进行了优化的版本。对于一个真正的实现你可能想看看内部方法的源代码sort1(int[], offset, length)
中的数组类。
我试图最大程度地将比较器与原始类型一起使用。最后我得出结论,没有办法欺骗比较器。这是我的实现。
public class ArrSortComptr {
public static void main(String[] args) {
int[] array = { 3, 2, 1, 5, 8, 6 };
int[] sortedArr=SortPrimitiveInt(new intComp(),array);
System.out.println("InPut "+ Arrays.toString(array));
System.out.println("OutPut "+ Arrays.toString(sortedArr));
}
static int[] SortPrimitiveInt(Comparator<Integer> com,int ... arr)
{
Integer[] objInt=intToObject(arr);
Arrays.sort(objInt,com);
return intObjToPrimitive(objInt);
}
static Integer[] intToObject(int ... arr)
{
Integer[] a=new Integer[arr.length];
int cnt=0;
for(int val:arr)
a[cnt++]=new Integer(val);
return a;
}
static int[] intObjToPrimitive(Integer ... arr)
{
int[] a=new int[arr.length];
int cnt=0;
for(Integer val:arr)
if(val!=null)
a[cnt++]=val.intValue();
return a;
}
}
class intComp implements Comparator<Integer>
{
@Override //your comparator implementation.
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o1.compareTo(o2);
}
}
@罗马:我不能说这是一个很好的例子,但是因为你问过这就是我的想法。假设在一个数组中您要根据其绝对值对数字进行排序。
Integer d1=Math.abs(o1);
Integer d2=Math.abs(o2);
return d1.compareTo(d2);
另一个例子可能是您只想对大于100的数字进行排序,这实际上取决于情况,我想不出更多的情况了,也许Alexandru可以给出更多的例子,因为他说他想对int数组使用比较器。
return sign * (i1 - i2);
,其中sign
为-1或+1取决于期望的顺序。
这是一些无需任何装箱/拆箱操作的代码(实际上不是我最初想的Timsort,但效果很好)。在我的测试中,它的工作速度比使用Collections.sort和数组周围的List包装器快了3-4倍。
// This code has been contributed by 29AjayKumar
// from: https://www.geeksforgeeks.org/sort/
static final int sortIntArrayWithComparator_RUN = 32;
// this function sorts array from left index to
// to right index which is of size atmost RUN
static void sortIntArrayWithComparator_insertionSort(int[] arr, IntComparator comparator, int left, int right) {
for (int i = left + 1; i <= right; i++)
{
int temp = arr[i];
int j = i - 1;
while (j >= left && comparator.compare(arr[j], temp) > 0)
{
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
}
}
// merge function merges the sorted runs
static void sortIntArrayWithComparator_merge(int[] arr, IntComparator comparator, int l, int m, int r) {
// original array is broken in two parts
// left and right array
int len1 = m - l + 1, len2 = r - m;
int[] left = new int[len1];
int[] right = new int[len2];
for (int x = 0; x < len1; x++)
{
left[x] = arr[l + x];
}
for (int x = 0; x < len2; x++)
{
right[x] = arr[m + 1 + x];
}
int i = 0;
int j = 0;
int k = l;
// after comparing, we merge those two array
// in larger sub array
while (i < len1 && j < len2)
{
if (comparator.compare(left[i], right[j]) <= 0)
{
arr[k] = left[i];
i++;
}
else
{
arr[k] = right[j];
j++;
}
k++;
}
// copy remaining elements of left, if any
while (i < len1)
{
arr[k] = left[i];
k++;
i++;
}
// copy remaining element of right, if any
while (j < len2)
{
arr[k] = right[j];
k++;
j++;
}
}
// iterative sort function to sort the
// array[0...n-1] (similar to merge sort)
static void sortIntArrayWithComparator(int[] arr, IntComparator comparator) { sortIntArrayWithComparator(arr, lIntArray(arr), comparator); }
static void sortIntArrayWithComparator(int[] arr, int n, IntComparator comparator) {
// Sort individual subarrays of size RUN
for (int i = 0; i < n; i += sortIntArrayWithComparator_RUN)
{
sortIntArrayWithComparator_insertionSort(arr, comparator, i, Math.min((i + 31), (n - 1)));
}
// start merging from size RUN (or 32). It will merge
// to form size 64, then 128, 256 and so on ....
for (int size = sortIntArrayWithComparator_RUN; size < n; size = 2 * size)
{
// pick starting point of left sub array. We
// are going to merge arr[left..left+size-1]
// and arr[left+size, left+2*size-1]
// After every merge, we increase left by 2*size
for (int left = 0; left < n; left += 2 * size)
{
// find ending point of left sub array
// mid+1 is starting point of right sub array
int mid = Math.min(left + size - 1, n - 1);
int right = Math.min(left + 2 * size - 1, n - 1);
// merge sub array arr[left.....mid] &
// arr[mid+1....right]
sortIntArrayWithComparator_merge(arr, comparator, left, mid, right);
}
}
}
static int lIntArray(int[] a) {
return a == null ? 0 : a.length;
}
static interface IntComparator {
int compare(int a, int b);
}