PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/jodd-core/src/main/java/jodd/util/sort/ComparableTimSort.java

https://bitbucket.org/cng1985/jodd
Java | 545 lines | 428 code | 70 blank | 47 comment | 126 complexity | d124b2ce351e852c333e86dcb5f2c878 MD5 | raw file
  1. // Copyright (c) 2003-2012, Jodd Team (jodd.org). All Rights Reserved.
  2. package jodd.util.sort;
  3. /**
  4. * <code>ComparableTimSort</code> from JDK7.
  5. * Changes:
  6. * <ul>
  7. * <li>reformatted</li>
  8. * <li>single sort method</li>
  9. * <li>no range check</li>
  10. * <li>asserts removed</li>
  11. * <li>comments removed</li>
  12. * </ul>
  13. *
  14. * @author Josh Bloch
  15. */
  16. public class ComparableTimSort {
  17. private static final int MIN_MERGE = 32;
  18. private final Object[] a;
  19. private static final int MIN_GALLOP = 7;
  20. private int minGallop = MIN_GALLOP;
  21. private static final int INITIAL_TMP_STORAGE_LENGTH = 256;
  22. private Object[] tmp;
  23. private int stackSize = 0; // Number of pending runs on stack
  24. private final int[] runBase;
  25. private final int[] runLen;
  26. private ComparableTimSort(Object[] a) {
  27. this.a = a;
  28. // Allocate temp storage (which may be increased later if necessary)
  29. int len = a.length;
  30. @SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
  31. Object[] newArray = new Object[len < 2 * INITIAL_TMP_STORAGE_LENGTH ?
  32. len >>> 1 : INITIAL_TMP_STORAGE_LENGTH];
  33. tmp = newArray;
  34. int stackLen = (len < 120 ? 5 :
  35. len < 1542 ? 10 :
  36. len < 119151 ? 19 : 40);
  37. runBase = new int[stackLen];
  38. runLen = new int[stackLen];
  39. }
  40. public static void sort(Object[] a) {
  41. int lo = 0;
  42. int hi = a.length;
  43. int nRemaining = hi - lo;
  44. if (nRemaining < 2)
  45. return; // Arrays of size 0 and 1 are always sorted
  46. // If array is small, do a "mini-TimSort" with no merges
  47. if (nRemaining < MIN_MERGE) {
  48. int initRunLen = countRunAndMakeAscending(a, lo, hi);
  49. binarySort(a, lo, hi, lo + initRunLen);
  50. return;
  51. }
  52. ComparableTimSort ts = new ComparableTimSort(a);
  53. int minRun = minRunLength(nRemaining);
  54. do {
  55. // Identify next run
  56. int runLen = countRunAndMakeAscending(a, lo, hi);
  57. // If run is short, extend to min(minRun, nRemaining)
  58. if (runLen < minRun) {
  59. int force = nRemaining <= minRun ? nRemaining : minRun;
  60. binarySort(a, lo, lo + force, lo + runLen);
  61. runLen = force;
  62. }
  63. // Push run onto pending-run stack, and maybe merge
  64. ts.pushRun(lo, runLen);
  65. ts.mergeCollapse();
  66. // Advance to find next run
  67. lo += runLen;
  68. nRemaining -= runLen;
  69. } while (nRemaining != 0);
  70. // Merge all remaining runs to complete sort
  71. ts.mergeForceCollapse();
  72. }
  73. @SuppressWarnings("fallthrough")
  74. private static void binarySort(Object[] a, int lo, int hi, int start) {
  75. if (start == lo)
  76. start++;
  77. for (; start < hi; start++) {
  78. @SuppressWarnings("unchecked")
  79. Comparable<Object> pivot = (Comparable) a[start];
  80. // Set left (and right) to the index where a[start] (pivot) belongs
  81. int left = lo;
  82. int right = start;
  83. while (left < right) {
  84. int mid = (left + right) >>> 1;
  85. if (pivot.compareTo(a[mid]) < 0)
  86. right = mid;
  87. else
  88. left = mid + 1;
  89. }
  90. int n = start - left; // The number of elements to move
  91. // Switch is just an optimization for arraycopy in default case
  92. switch (n) {
  93. case 2:
  94. a[left + 2] = a[left + 1];
  95. case 1:
  96. a[left + 1] = a[left];
  97. break;
  98. default:
  99. System.arraycopy(a, left, a, left + 1, n);
  100. }
  101. a[left] = pivot;
  102. }
  103. }
  104. @SuppressWarnings("unchecked")
  105. private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {
  106. int runHi = lo + 1;
  107. if (runHi == hi)
  108. return 1;
  109. // Find end of run, and reverse range if descending
  110. if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending
  111. while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)
  112. runHi++;
  113. reverseRange(a, lo, runHi);
  114. } else { // Ascending
  115. while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0)
  116. runHi++;
  117. }
  118. return runHi - lo;
  119. }
  120. private static void reverseRange(Object[] a, int lo, int hi) {
  121. hi--;
  122. while (lo < hi) {
  123. Object t = a[lo];
  124. a[lo++] = a[hi];
  125. a[hi--] = t;
  126. }
  127. }
  128. private static int minRunLength(int n) {
  129. int r = 0; // Becomes 1 if any 1 bits are shifted off
  130. while (n >= MIN_MERGE) {
  131. r |= (n & 1);
  132. n >>= 1;
  133. }
  134. return n + r;
  135. }
  136. private void pushRun(int runBase, int runLen) {
  137. this.runBase[stackSize] = runBase;
  138. this.runLen[stackSize] = runLen;
  139. stackSize++;
  140. }
  141. private void mergeCollapse() {
  142. while (stackSize > 1) {
  143. int n = stackSize - 2;
  144. if (n > 0 && runLen[n - 1] <= runLen[n] + runLen[n + 1]) {
  145. if (runLen[n - 1] < runLen[n + 1])
  146. n--;
  147. mergeAt(n);
  148. } else if (runLen[n] <= runLen[n + 1]) {
  149. mergeAt(n);
  150. } else {
  151. break; // Invariant is established
  152. }
  153. }
  154. }
  155. private void mergeForceCollapse() {
  156. while (stackSize > 1) {
  157. int n = stackSize - 2;
  158. if (n > 0 && runLen[n - 1] < runLen[n + 1])
  159. n--;
  160. mergeAt(n);
  161. }
  162. }
  163. @SuppressWarnings("unchecked")
  164. private void mergeAt(int i) {
  165. int base1 = runBase[i];
  166. int len1 = runLen[i];
  167. int base2 = runBase[i + 1];
  168. int len2 = runLen[i + 1];
  169. runLen[i] = len1 + len2;
  170. if (i == stackSize - 3) {
  171. runBase[i + 1] = runBase[i + 2];
  172. runLen[i + 1] = runLen[i + 2];
  173. }
  174. stackSize--;
  175. int k = gallopRight((Comparable<Object>) a[base2], a, base1, len1, 0);
  176. base1 += k;
  177. len1 -= k;
  178. if (len1 == 0)
  179. return;
  180. len2 = gallopLeft((Comparable<Object>) a[base1 + len1 - 1], a,
  181. base2, len2, len2 - 1);
  182. if (len2 == 0)
  183. return;
  184. // Merge remaining runs, using tmp array with min(len1, len2) elements
  185. if (len1 <= len2)
  186. mergeLo(base1, len1, base2, len2);
  187. else
  188. mergeHi(base1, len1, base2, len2);
  189. }
  190. private static int gallopLeft(Comparable<Object> key, Object[] a,
  191. int base, int len, int hint) {
  192. int lastOfs = 0;
  193. int ofs = 1;
  194. if (key.compareTo(a[base + hint]) > 0) {
  195. // Gallop right until a[base+hint+lastOfs] < key <= a[base+hint+ofs]
  196. int maxOfs = len - hint;
  197. while (ofs < maxOfs && key.compareTo(a[base + hint + ofs]) > 0) {
  198. lastOfs = ofs;
  199. ofs = (ofs << 1) + 1;
  200. if (ofs <= 0) // int overflow
  201. ofs = maxOfs;
  202. }
  203. if (ofs > maxOfs)
  204. ofs = maxOfs;
  205. // Make offsets relative to base
  206. lastOfs += hint;
  207. ofs += hint;
  208. } else { // key <= a[base + hint]
  209. // Gallop left until a[base+hint-ofs] < key <= a[base+hint-lastOfs]
  210. final int maxOfs = hint + 1;
  211. while (ofs < maxOfs && key.compareTo(a[base + hint - ofs]) <= 0) {
  212. lastOfs = ofs;
  213. ofs = (ofs << 1) + 1;
  214. if (ofs <= 0) // int overflow
  215. ofs = maxOfs;
  216. }
  217. if (ofs > maxOfs)
  218. ofs = maxOfs;
  219. // Make offsets relative to base
  220. int tmp = lastOfs;
  221. lastOfs = hint - ofs;
  222. ofs = hint - tmp;
  223. }
  224. lastOfs++;
  225. while (lastOfs < ofs) {
  226. int m = lastOfs + ((ofs - lastOfs) >>> 1);
  227. if (key.compareTo(a[base + m]) > 0)
  228. lastOfs = m + 1; // a[base + m] < key
  229. else
  230. ofs = m; // key <= a[base + m]
  231. }
  232. return ofs;
  233. }
  234. private static int gallopRight(Comparable<Object> key, Object[] a,
  235. int base, int len, int hint) {
  236. int ofs = 1;
  237. int lastOfs = 0;
  238. if (key.compareTo(a[base + hint]) < 0) {
  239. // Gallop left until a[b+hint - ofs] <= key < a[b+hint - lastOfs]
  240. int maxOfs = hint + 1;
  241. while (ofs < maxOfs && key.compareTo(a[base + hint - ofs]) < 0) {
  242. lastOfs = ofs;
  243. ofs = (ofs << 1) + 1;
  244. if (ofs <= 0) // int overflow
  245. ofs = maxOfs;
  246. }
  247. if (ofs > maxOfs)
  248. ofs = maxOfs;
  249. // Make offsets relative to b
  250. int tmp = lastOfs;
  251. lastOfs = hint - ofs;
  252. ofs = hint - tmp;
  253. } else { // a[b + hint] <= key
  254. // Gallop right until a[b+hint + lastOfs] <= key < a[b+hint + ofs]
  255. int maxOfs = len - hint;
  256. while (ofs < maxOfs && key.compareTo(a[base + hint + ofs]) >= 0) {
  257. lastOfs = ofs;
  258. ofs = (ofs << 1) + 1;
  259. if (ofs <= 0) // int overflow
  260. ofs = maxOfs;
  261. }
  262. if (ofs > maxOfs)
  263. ofs = maxOfs;
  264. // Make offsets relative to b
  265. lastOfs += hint;
  266. ofs += hint;
  267. }
  268. lastOfs++;
  269. while (lastOfs < ofs) {
  270. int m = lastOfs + ((ofs - lastOfs) >>> 1);
  271. if (key.compareTo(a[base + m]) < 0)
  272. ofs = m; // key < a[b + m]
  273. else
  274. lastOfs = m + 1; // a[b + m] <= key
  275. }
  276. return ofs;
  277. }
  278. @SuppressWarnings("unchecked")
  279. private void mergeLo(int base1, int len1, int base2, int len2) {
  280. // Copy first run into temp array
  281. Object[] a = this.a; // For performance
  282. Object[] tmp = ensureCapacity(len1);
  283. System.arraycopy(a, base1, tmp, 0, len1);
  284. int cursor1 = 0; // Indexes into tmp array
  285. int cursor2 = base2; // Indexes int a
  286. int dest = base1; // Indexes int a
  287. // Move first element of second run and deal with degenerate cases
  288. a[dest++] = a[cursor2++];
  289. if (--len2 == 0) {
  290. System.arraycopy(tmp, cursor1, a, dest, len1);
  291. return;
  292. }
  293. if (len1 == 1) {
  294. System.arraycopy(a, cursor2, a, dest, len2);
  295. a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge
  296. return;
  297. }
  298. int minGallop = this.minGallop; // Use local variable for performance
  299. outer:
  300. while (true) {
  301. int count1 = 0; // Number of times in a row that first run won
  302. int count2 = 0; // Number of times in a row that second run won
  303. /*
  304. * Do the straightforward thing until (if ever) one run starts
  305. * winning consistently.
  306. */
  307. do {
  308. if (((Comparable) a[cursor2]).compareTo(tmp[cursor1]) < 0) {
  309. a[dest++] = a[cursor2++];
  310. count2++;
  311. count1 = 0;
  312. if (--len2 == 0)
  313. break outer;
  314. } else {
  315. a[dest++] = tmp[cursor1++];
  316. count1++;
  317. count2 = 0;
  318. if (--len1 == 1)
  319. break outer;
  320. }
  321. } while ((count1 | count2) < minGallop);
  322. /*
  323. * One run is winning so consistently that galloping may be a
  324. * huge win. So try that, and continue galloping until (if ever)
  325. * neither run appears to be winning consistently anymore.
  326. */
  327. do {
  328. count1 = gallopRight((Comparable) a[cursor2], tmp, cursor1, len1, 0);
  329. if (count1 != 0) {
  330. System.arraycopy(tmp, cursor1, a, dest, count1);
  331. dest += count1;
  332. cursor1 += count1;
  333. len1 -= count1;
  334. if (len1 <= 1) // len1 == 1 || len1 == 0
  335. break outer;
  336. }
  337. a[dest++] = a[cursor2++];
  338. if (--len2 == 0)
  339. break outer;
  340. count2 = gallopLeft((Comparable) tmp[cursor1], a, cursor2, len2, 0);
  341. if (count2 != 0) {
  342. System.arraycopy(a, cursor2, a, dest, count2);
  343. dest += count2;
  344. cursor2 += count2;
  345. len2 -= count2;
  346. if (len2 == 0)
  347. break outer;
  348. }
  349. a[dest++] = tmp[cursor1++];
  350. if (--len1 == 1)
  351. break outer;
  352. minGallop--;
  353. } while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
  354. if (minGallop < 0)
  355. minGallop = 0;
  356. minGallop += 2; // Penalize for leaving gallop mode
  357. } // End of "outer" loop
  358. this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field
  359. if (len1 == 1) {
  360. System.arraycopy(a, cursor2, a, dest, len2);
  361. a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge
  362. } else if (len1 == 0) {
  363. throw new IllegalArgumentException(
  364. "Comparison method violates its general contract!");
  365. } else {
  366. System.arraycopy(tmp, cursor1, a, dest, len1);
  367. }
  368. }
  369. @SuppressWarnings("unchecked")
  370. private void mergeHi(int base1, int len1, int base2, int len2) {
  371. // Copy second run into temp array
  372. Object[] a = this.a; // For performance
  373. Object[] tmp = ensureCapacity(len2);
  374. System.arraycopy(a, base2, tmp, 0, len2);
  375. int cursor1 = base1 + len1 - 1; // Indexes into a
  376. int cursor2 = len2 - 1; // Indexes into tmp array
  377. int dest = base2 + len2 - 1; // Indexes into a
  378. // Move last element of first run and deal with degenerate cases
  379. a[dest--] = a[cursor1--];
  380. if (--len1 == 0) {
  381. System.arraycopy(tmp, 0, a, dest - (len2 - 1), len2);
  382. return;
  383. }
  384. if (len2 == 1) {
  385. dest -= len1;
  386. cursor1 -= len1;
  387. System.arraycopy(a, cursor1 + 1, a, dest + 1, len1);
  388. a[dest] = tmp[cursor2];
  389. return;
  390. }
  391. int minGallop = this.minGallop; // Use local variable for performance
  392. outer:
  393. while (true) {
  394. int count1 = 0; // Number of times in a row that first run won
  395. int count2 = 0; // Number of times in a row that second run won
  396. do {
  397. if (((Comparable) tmp[cursor2]).compareTo(a[cursor1]) < 0) {
  398. a[dest--] = a[cursor1--];
  399. count1++;
  400. count2 = 0;
  401. if (--len1 == 0)
  402. break outer;
  403. } else {
  404. a[dest--] = tmp[cursor2--];
  405. count2++;
  406. count1 = 0;
  407. if (--len2 == 1)
  408. break outer;
  409. }
  410. } while ((count1 | count2) < minGallop);
  411. do {
  412. count1 = len1 - gallopRight((Comparable) tmp[cursor2], a, base1, len1, len1 - 1);
  413. if (count1 != 0) {
  414. dest -= count1;
  415. cursor1 -= count1;
  416. len1 -= count1;
  417. System.arraycopy(a, cursor1 + 1, a, dest + 1, count1);
  418. if (len1 == 0)
  419. break outer;
  420. }
  421. a[dest--] = tmp[cursor2--];
  422. if (--len2 == 1)
  423. break outer;
  424. count2 = len2 - gallopLeft((Comparable) a[cursor1], tmp, 0, len2, len2 - 1);
  425. if (count2 != 0) {
  426. dest -= count2;
  427. cursor2 -= count2;
  428. len2 -= count2;
  429. System.arraycopy(tmp, cursor2 + 1, a, dest + 1, count2);
  430. if (len2 <= 1)
  431. break outer; // len2 == 1 || len2 == 0
  432. }
  433. a[dest--] = a[cursor1--];
  434. if (--len1 == 0)
  435. break outer;
  436. minGallop--;
  437. } while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
  438. if (minGallop < 0)
  439. minGallop = 0;
  440. minGallop += 2; // Penalize for leaving gallop mode
  441. } // End of "outer" loop
  442. this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field
  443. if (len2 == 1) {
  444. dest -= len1;
  445. cursor1 -= len1;
  446. System.arraycopy(a, cursor1 + 1, a, dest + 1, len1);
  447. a[dest] = tmp[cursor2]; // Move first elt of run2 to front of merge
  448. } else if (len2 == 0) {
  449. throw new IllegalArgumentException(
  450. "Comparison method violates its general contract!");
  451. } else {
  452. System.arraycopy(tmp, 0, a, dest - (len2 - 1), len2);
  453. }
  454. }
  455. private Object[] ensureCapacity(int minCapacity) {
  456. if (tmp.length < minCapacity) {
  457. // Compute smallest power of 2 > minCapacity
  458. int newSize = minCapacity;
  459. newSize |= newSize >> 1;
  460. newSize |= newSize >> 2;
  461. newSize |= newSize >> 4;
  462. newSize |= newSize >> 8;
  463. newSize |= newSize >> 16;
  464. newSize++;
  465. if (newSize < 0) // Not bloody likely!
  466. newSize = minCapacity;
  467. else
  468. newSize = Math.min(newSize, a.length >>> 1);
  469. @SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
  470. Object[] newArray = new Object[newSize];
  471. tmp = newArray;
  472. }
  473. return tmp;
  474. }
  475. }