From 605701e40a90538fcf220e3239604dd0618340d6 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 4 Nov 2013 00:27:04 -0600 Subject: [PATCH] Replace Arrays.sort() with an efficient sort algorithm This change reuses the existing insertion sort (which was previously what Arrays.sort() executed) in a full intro sort pipeline. The implementation is based on the Musser paper on intro sort (Musser, David R. "Introspective sorting and selection algorithms." Softw., Pract. Exper. 27.8 (1997): 983-993.) and Wikipedia's current description of the heap sort: http://en.wikipedia.org/wiki/Heapsort. Signed-off-by: Johannes Schindelin --- classpath/java/util/Arrays.java | 94 ++++++++++++++++++++++++++++++++- test/Arrays.java | 37 +++++++++++++ 2 files changed, 130 insertions(+), 1 deletion(-) diff --git a/classpath/java/util/Arrays.java b/classpath/java/util/Arrays.java index 786ab091f3..73a978d4bc 100644 --- a/classpath/java/util/Arrays.java +++ b/classpath/java/util/Arrays.java @@ -148,8 +148,100 @@ public class Arrays { }); } + private final static int SORT_SIZE_THRESHOLD = 16; + public static void sort(T[] array, Comparator comparator) { - // insertion sort + introSort(array, comparator, 0, array.length, array.length); + insertionSort(array, comparator); + } + + private static void introSort(T[] array, + Comparator comparator, int begin, int end, int limit) + { + while (end - begin > SORT_SIZE_THRESHOLD) { + if (limit == 0) { + heapSort(array, comparator, begin, end); + return; + } + limit >>= 1; + + // median of three + T a = array[begin]; + T b = array[begin + (end - begin) / 2 + 1]; + T c = array[end - 1]; + T median; + if (comparator.compare(a, b) < 0) { + median = comparator.compare(b, c) < 0 ? + b : (comparator.compare(a, c) < 0 ? c : a); + } else { + median = comparator.compare(b, c) > 0 ? + b : (comparator.compare(a, c) > 0 ? c : a); + } + + // partition + int pivot, i = begin, j = end; + for (;;) { + while (comparator.compare(array[i], median) < 0) { + ++i; + } + --j; + while (comparator.compare(median, array[j]) < 0) { + --j; + } + if (i >= j) { + pivot = i; + break; + } + T swap = array[i]; + array[i] = array[j]; + array[j] = swap; + ++i; + } + + introSort(array, comparator, pivot, end, limit); + end = pivot; + } + } + + private static void heapSort(T[] array, Comparator comparator, + int begin, int end) + { + int count = end - begin; + for (int i = count / 2 - 1; i >= 0; --i) { + siftDown(array, comparator, i, count, begin); + } + for (int i = count - 1; i > 0; --i) { + // swap begin and begin + i + T swap = array[begin + i]; + array[begin + i] = array[begin]; + array[begin] = swap; + + siftDown(array, comparator, 0, i, begin); + } + } + + private static void siftDown(T[] array, Comparator comparator, + int i, int count, int offset) + { + T value = array[offset + i]; + while (i < count / 2) { + int child = 2 * i + 1; + if (child + 1 < count && + comparator.compare(array[child], array[child + 1]) < 0) { + ++child; + } + if (comparator.compare(value, array[child]) >= 0) { + break; + } + array[offset + i] = array[offset + child]; + i = child; + } + array[offset + i] = value; + } + + private static void insertionSort(T[] array, + Comparator comparator) + { for (int j = 1; j < array.length; ++j) { T t = array[j]; int i = j - 1; diff --git a/test/Arrays.java b/test/Arrays.java index 42e958068a..5a2ad8bf0a 100644 --- a/test/Arrays.java +++ b/test/Arrays.java @@ -3,6 +3,41 @@ public class Arrays { if (! v) throw new RuntimeException(); } + private static > void expectSorted(T[] array) { + for (int i = 1; i < array.length; ++i) { + expect(array[i - 1].compareTo(array[i]) <= 0); + } + } + + private static int pseudoRandom(int seed) { + return 3170425 * seed + 132102; + } + + private static > int shuffle(T[] array, int seed) { + for (int i = array.length; i > 1; --i) { + int i2 = (seed < 0 ? -seed : seed) % i; + T value = array[i - 1]; + array[i - 1] = array[i2]; + array[i2] = value; + seed = pseudoRandom(seed); + } + return seed; + } + + public static void testSort() { + Integer[] array = new Integer[64]; + for (int i = 0; i < array.length; ++i) { + array[i] = Integer.valueOf(i + 1); + } + ; + int random = 12345; + for (int i = 0; i < 32; ++i) { + random = shuffle(array, random); + java.util.Arrays.sort(array); + expectSorted(array); + } + } + public static void main(String[] args) { { int[] array = new int[0]; Exception exception = null; @@ -94,5 +129,7 @@ public class Arrays { java.util.Arrays.hashCode(a); java.util.Arrays.hashCode((Object[])null); } + + testSort(); } }