Ich muss ein Array von Ints mit einem benutzerdefinierten Komparator sortieren, aber die Java-Bibliothek bietet keine Sortierfunktion für Ints mit Komparatoren (Komparatoren können nur mit Objekten verwendet werden). Gibt es eine einfache Möglichkeit, dies zu tun?
75
Antworten:
Wenn Sie den Typ Ihres Eingabearrays nicht ändern können, funktioniert Folgendes:
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);
Dies wird
ArrayUtils
aus dem commons-lang-Projekt verwendet, um einfach zwischenint[]
und zu konvertierenInteger[]
, eine Kopie des Arrays zu erstellen, die Sortierung durchzuführen und dann die sortierten Daten über das Original zu kopieren.quelle
return o2.compareTo(o1);
ist das richtig? Ich glaube auf diese Weise wird die Reihenfolge umgekehrt, wie wir erwarten ...int
Wie wäre es mit Streams (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();
Oder an Ort und Stelle:
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 );
quelle
(a, b) -> b - a
umgekehrter Reihenfolge verwenden. Dieser Komparator kann überlaufen. Kümmere dich um die Existenz vonComparator.reverseOrder()
…Wenn Sie das Array nicht kopieren möchten (sagen wir, es ist sehr groß), möchten Sie möglicherweise einen Wrapper erstellen
List<Integer>
, der in einer Sortierung verwendet werden kann: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; } };
Und jetzt können Sie diese Wrapper-Liste mit einem benutzerdefinierten Komparator sortieren.
quelle
sort
kopiert die gesamte Liste in ein Array, sortiert sie und schreibt sie zurück. Und da diese Liste denRandomAccess
Marker nicht implementiert , wird beim Zurückschreiben ein verwendet,ListIterator
anstatt nur aufzurufenset
.LinkedList
wäre es super schlecht direkt zu sortieren, also machen sie eine Kopie. Warum sie nicht suchen,RandomAccess
ist nicht klar, ich denke, nicht viele Leute kennen diese Marker-Oberfläche überhaupt.RandomAccess
würde nicht schaden, wenn diese Optimierung irgendwann in der Zukunft durchgeführt wird. Derzeit erreicht die Methode jedoch nicht das, wofür sie eingerichtet wurde.Sie können
IntArrays.quickSort(array, comparator)
aus der Fastutil-Bibliothek verwenden.quelle
Sie benötigen keine externe Bibliothek:
Integer[] input = Arrays.stream(arr).boxed().toArray(Integer[]::new); Arrays.sort(input, (a, b) -> b - a); // reverse order return Arrays.stream(input).mapToInt(Integer::intValue).toArray();
quelle
Indem Sie Ihr int-Array in ein Integer-Array umwandeln und dann verwenden
public static <T> void Arrays.sort(T[] a, Comparator<? super T> c)
(der erste Schritt ist nur erforderlich, da ich befürchte, dass Autoboxing möglicherweise auf Arrays funktioniert).quelle
Hier ist eine Hilfsmethode, um die Arbeit zu erledigen.
Zunächst benötigen Sie eine neue Comparator-Oberfläche, da Comparator keine Grundelemente unterstützt:
public interface IntComparator{ public int compare(int a, int b); }
(Sie könnten es natürlich mit Autoboxing / Unboxing machen, aber ich werde nicht dorthin gehen, das ist hässlich)
Hier ist eine Hilfsmethode zum Sortieren eines int-Arrays mit diesem Komparator:
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; } } }
Und hier ist ein Client-Code. Ein dummer Komparator, der alle Zahlen sortiert, die nur aus der Ziffer '9' nach vorne bestehen (wieder nach Größe sortiert) und dann den Rest (was auch immer gut ist):
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));
Ausgabe:
[9, 99, 999, 9999, 32, 65, 292, 433, 544, 4343, 44934343]
Der Sortiercode wurde aus Arrays.sort (int []) übernommen , und ich habe nur die Version verwendet, die für kleine Arrays optimiert ist. Für eine echte Implementierung sollten Sie sich wahrscheinlich den Quellcode der internen Methode
sort1(int[], offset, length)
in der Arrays- Klasse ansehen .quelle
Ich habe maximal versucht, den Komparator mit primitivem Typ selbst zu verwenden. Endlich kam ich zu dem Schluss, dass es keine Möglichkeit gibt, den Komparator zu betrügen. Dies ist meine Implementierung.
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); } }
@Roman: Ich kann nicht sagen, dass dies ein gutes Beispiel ist, aber seit du gefragt hast, ist mir das in den Sinn gekommen. Angenommen, Sie möchten in einem Array Zahlen nur nach ihrem absoluten Wert sortieren.
Integer d1=Math.abs(o1); Integer d2=Math.abs(o2); return d1.compareTo(d2);
Ein anderes Beispiel kann sein, dass Sie nur Zahlen größer als 100 sortieren möchten. Es hängt tatsächlich von der Situation ab. Ich kann mir keine weiteren Situationen vorstellen. Vielleicht kann Alexandru weitere Beispiele nennen, da er sagt, er möchte einen Komparator für das int-Array verwenden .
quelle
return sign * (i1 - i2);
wosign
-1 oder +1 ist, abhängig von der gewünschten Reihenfolge.Hier ist ein Code (es ist eigentlich nicht Timsort, wie ich ursprünglich dachte, aber es funktioniert gut), der den Trick ohne Boxen / Unboxing macht. In meinen Tests funktioniert es 3-4 mal schneller als die Verwendung von Collections.sort mit einem List-Wrapper um das Array.
// 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); }
quelle