Collections Data Structure Java

/*
 * Copyright 2004, 2005, 2006 Odysseus Software GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ 
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
/**
 * Utility class providing some useful static sort methods. The sort routines
 * all return index permutations p such that data[p[0]],..,data[p[data.length-1]]
 * is in sorted order. The data array itself is not modified.
 * To actually rearrange the array elements, the inverse of p can be used to
 * permute the array, such that data[0],..,data[data.length-1] is in sorted
 * order. Use getIterator(p, data) to iterate in sorted order.
 * A code example may show you what to do next:
 * 

 * String[] colors = { "red", "green", "blue" };
 * int[] p = SortUtils.sort(colors, new StringComparator());
 * // --> (colors[p[0]], colors[p[1]], colors[p[2]]) == ("blue","green","red")
 * Iterator iter = SortUtils.getIterator(p, colors)
 * // --> (iter.next(), iter.next(), iter.next()) == ("blue","green","red")
 * SortUtils.permute(SortUtils.inverse(p), colors, true);
 * // --> (colors[0], colors[1], colors[2]) == ("blue","green","red")
 * 

 * Stable sorts (preserving order of equal elements) are supported.
 * Sorting is done using quick-sort mith median of 3 (and insertion-sort
 * for small ranges).
 *
 * @author Christoph Beck
 */
public class SortUtils {
  /**
   * Helper class used to perform quicksort.
   *
   * @author Christoph Beck
   */
  static final class QuickSorter {
    private static final int INSERTIONSORT_THRESHOLD = 7;
    private final Object[] data;
    QuickSorter(Object[] data) {
      this.data = data;
    }
    private int compare(Comparator cmp, boolean stable, int i, int j) {
      int result = cmp.compare(data[i], data[j]);
      if (result == 0 && stable && i != j) {
        result = i < j ? -1 : 1;
      }
      return result;
    }
    private int med3(Comparator cmp, int a, int b, int c) {
        return  (compare(cmp, false, a, b) < 0 ?
            (compare(cmp, false, b, c) < 0 ? b : compare(cmp, false, a, c) < 0 ? c : a) :
            (compare(cmp, false, b, c) > 0 ? b : compare(cmp, false, a, c) < 0 ? c : a));
    }
    private int pivot(int[] indices, Comparator cmp, int lo, int hi) {
      return med3(cmp, indices[lo + 1], indices[(lo + hi) / 2], indices[hi - 1]);
    }
    private void swap(int[] indices, int i, int j) {
      int tmp = indices[i];
      indices[i] = indices[j];
      indices[j] = tmp;
    }
    private void insertionSort(int[] indices, Comparator cmp, boolean stable, int lo, int hi) {
      for (int i = lo; i <= hi; i++) {
            for (int j = i; j > lo && compare(cmp, stable, indices[j-1], indices[j]) > 0; j--) {
              swap(indices, j-1, j);
            }
          }
    }
    private void quickSort(int[] indices, Comparator cmp, boolean stable, int lo0, int hi0) {
      int pivot = pivot(indices, cmp, lo0, hi0);
      int lo = lo0, hi = hi0;
      while (lo <= hi) {
        while (lo < hi0 && compare(cmp, stable, pivot, indices[lo]) > 0)
          ++lo;
        while (hi > lo0 && compare(cmp, stable, pivot, indices[hi]) < 0)
          --hi;
        if (lo <= hi) {
          swap(indices, lo++, hi--);
        }
      }
      sort(indices, cmp, stable, lo0, hi);
      sort(indices, cmp, stable, lo, hi0);
    }
    void sort(int[] indices, Comparator cmp, boolean stable, int lo, int hi) {
      if (hi - lo < INSERTIONSORT_THRESHOLD) {
        insertionSort(indices, cmp, stable, lo, hi);
      } else {
        quickSort(indices, cmp, stable, lo, hi);
      }
    }
    void sort(int[] indices, Comparator cmp, boolean stable) {
      sort(indices, cmp, stable, 0, indices.length - 1);
    }
    int[] sort(Comparator cmp, boolean stable) {
      int[] indices = identity(data.length);
      sort(indices, cmp, stable);
      return indices;
    }
  }
  /**
   * Create identity permutation, that is {0, 1, ..., n}
   */
  public static int[] identity(int n) {
    int[] indices = new int[n];
    for (int i = 0; i < n; i++)
      indices[i] = i;
    return indices;
  }
  /**
   * Create reverse permutation, that is {n-1, .... 1, 0}
   */
  public static int[] reverse(int n) {
    int[] indices = new int[n];
    for (int i = 0; i < n; i++)
      indices[i] = n - i - 1;
    return indices;
  }
  /**
   * Compute inverse permutation
   */
  public static int[] inverse(int[] p) {
    int[] pi = new int[p.length];
    for (int i = 0; i < pi.length; i++)
      pi[p[i]] = i;
    return pi;
  }
  /**
   * Rearrange the specified data according to the specified permutation.
   * That is, the array is rearranged, such that
   * data_after[p[i]] == data_before[i].
   * @param data data to be permuted
   * @param p the permutation
   * @param clone if true, rearrange a clone instead of the original data;
   * @return the permuted array (which is the original reference if clone == false)
   */
  public static Object[] permute(int[] p, Object[] data, boolean clone) {
    Object[] permuted = null;
    if (clone) {
      permuted = (Object[])data.clone();
      for (int i = 0; i < data.length; i++)
        permuted[p[i]] = data[i];
    } else {
      // run thru cycles
      int i = 0;
      while (i < p.length) {
        if (p[i] < 0 || p[i] == i) // skip already handled and cycles of length 1
          ++i;
        else { // start a new cycle
          int j = p[i];
          Object save = data[i];
          while (p[j] >= 0) {
            Object tmp = data[j];
            data[j] = save;
            save = tmp;
            i = j;
            j = p[j];
            p[i] = -1;
          }
        }
      }
      permuted = data;
    }
    return permuted;
  }
  /**
   * Answer iterator, which iterates over specified data array according
   * to the specified permutation, that is
   * data[p[0]],..,data[p[data.length-1]]
   */
  public static Iterator getIterator(final int[] p, final Object[] data) {
    return new Iterator() {
      int pos = 0;
      public boolean hasNext() {
        return pos < data.length;
      }
      public Object next() {
        return data[p[pos++]];
      }
      public void remove() {
        throw new UnsupportedOperationException("Cannot remove from immutable iterator!");
      }
    };
  }
  /**
   * Answer iterator, which iterates over specified data list according
   * to the specified permutation, that is
   * data.get(p[0]),..,data.get(p[data.length-1])
   */
  public static Iterator getIterator(final int[] p, final List data) {
    return new Iterator() {
      int pos = 0;
      public boolean hasNext() {
        return pos < data.size();
      }
      public Object next() {
        return data.get(p[pos++]);
      }
      public void remove() {
        throw new UnsupportedOperationException("Cannot remove from immutable iterator!");
      }
    };
  }
//  /**
//   * An improved heap builder.
//   * Assumes children of i at 2i and 2i+1 (requires i>0)
//   */
//  private static void cheap(int[] indices, Object[] data, Comparator comparator, int i, int j) {
//    int k = (i << 1);
//    if (k > j)
//      return;
//    while (k < j) {
//      if (comparator.compare(data[indices[k]], data[indices[k + 1]]) < 0)
//        k++;
//      k <<= 1;
//    }
//    if (k > j)
//      k >>= 1;
//    while (comparator.compare(data[indices[k]], data[indices[i]]) < 0)
//      k >>= 1;
//    int t1 = indices[i], t2;
//    while (k > i) {
//      t2 = indices[k];
//      indices[k] = t1;
//      k >>= 1;
//      t1 = indices[k];
//      indices[k] = t2;
//      k >>= 1;
//    }
//    if (k == i)
//      indices[i] = t1;
//  }
//
//  /**
//   * Do a (clever) heapsort.
//   *
//   * @param comparator Comparator object specifying the sort order.
//   */
//  public static void cheapSort(int[] indices, Object[] data, Comparator comparator) {
//    int n = data.length;
//    if (n > 1) {
//      int i;
//      int m = 0;
//      for (i = 1; i < n; i++)
//        if (comparator.compare(data[indices[i]], data[indices[m]]) < 0)
//          m = i;
//      if (m > 0) {
//        int t = indices[0];
//        indices[0] = indices[m];
//        indices[m] = t;
//      }
//      if (n > 2) {
//        for (i = n / 2; i > 1; i--)
//          cheap(indices, data, comparator, i, n - 1);
//        for (i = n - 1; i > 1; i--) {
//          cheap(indices, data, comparator, 1, i);
//          int t = indices[1];
//          indices[1] = indices[i];
//          indices[i] = t;
//        }
//      }
//    }
//  }
//
//  /**
//   * Perform a cheapsort
//   */
//  public static int[] cheapSort(Object[] data, Comparator comparator) {
//    int[] indices = identity(data.length);
//    cheapSort(indices, data, comparator);
//    return indices;
//  }
  /**
   * Do a sort on indices.
   * @param data data to be sorted
   * @param comparator comparator to use
   * @param stable do a stable sort iff true
   * @param indices into data (any permutation of 0,..data.length-1).
   */
  public static void sort(int[] indices, Object[] data, Comparator comparator, boolean stable) {
    new QuickSorter(data).sort(indices, comparator, stable);
  }
  /**
   * Do a sort on indices.
   * @param data data to be sorted
   * @param comparator comparator to use
   * @param stable do a stable sort iff true
   * @return permutation p such that data[p[0]],..,data[p[data.length-1]] is in sorted order
   */
  public static int[] sort(Object[] data, Comparator comparator, boolean stable) {
    int[] indices = identity(data.length);
    sort(indices, data, comparator, stable);
    return indices;
  }
  /**
   * Do an unstable sort.
   * @param data data to be sorted
   * @param comparator comparator to use
   * @return permutation p such that data[p[0]],..,data[p[data.length-1]] is in sorted order
   */
  public static int[] sort(Object[] data, Comparator comparator) {
    return sort(data, comparator, false);
  }
  /**
   * Do an unstable sort.
   * @param data data to be sorted
   * @param indices into data (permutation of 0,..data.length-1).
   */
  public static void sort(int[] indices, Object[] data, Comparator comparator) {
    sort(indices, data, comparator, false);
  }
  /**
   * Test method
   */
  public static void main(String[] args) {
    Comparator cmp = new Comparator() {
      public int compare(Object o1, Object o2) {
        return ((Comparable)o1).compareTo(o2);
      }
    };
    int n = 1000000;
    if (args.length == 1)
      try {
        n = Integer.parseInt(args[0]);
      } catch (Exception e) {
        System.err.println(e);
      }
    System.out.println("Generating " + n + " random integers...");
    java.util.Random random = new java.util.Random();
    Integer[] data = new Integer[n];
    for (int i = 0; i < n; i++) {
      data[i] = new Integer(Math.abs(random.nextInt()));
//      data[i] = new Integer(i);
    }
    int[] indices;
    long time;
    System.out.print("Arrays.sort...");
    time = System.currentTimeMillis();
    Integer[] clone = (Integer[])data.clone();
    Arrays.sort(clone, cmp);
    System.out.println(System.currentTimeMillis()-time  + "ms");
    System.out.print("quicksort...");
    indices = identity(n);
    time = System.currentTimeMillis();
    sort(indices, data, cmp, false);
    System.out.println(System.currentTimeMillis()-time  + "ms");
    for (int i = 1; i < n; i++)
      if (cmp.compare(data[indices[i-1]], data[indices[i]]) > 0)
        System.err.println("proplem: quickSort at " + i);
    System.out.print("quicksort stable...");
//    indices = identity(n);
    time = System.currentTimeMillis();
    sort(indices, data, cmp, true);
    System.out.println(System.currentTimeMillis()-time + "ms");
    for (int i = 1; i < n; i++) {
      int res = cmp.compare(data[indices[i-1]], data[indices[i]]);
      if (res > 0)
        System.err.println("proplem: quickSort stable at " + i);
      if (res == 0 && indices[i-1] > indices[i])
        System.err.println("proplem: quickSort stable (not stable) at " + i);
    }
//    System.out.print("cheapsort...");
//    time = System.currentTimeMillis();
//    indices = cheapSort(data, cmp);
//    System.out.println(System.currentTimeMillis()-time + "ms");
//    for (int i = 1; i < n; i++)
//      if (cmp.compare(data[indices[i-1]], data[indices[i]]) > 0)
//        System.err.println("proplem: cheapSort at " + i);
  
    System.out.print("permutate copy...");
    time = System.currentTimeMillis();
    Object[] data_copy = permute(inverse(indices), data, true);
    System.out.println(System.currentTimeMillis()-time + "ms");
    for (int i = 1; i < n; i++)
      if (cmp.compare(data_copy[i-1], data_copy[i]) > 0)
        System.err.println("proplem: permute copy at " + i);
    System.out.print("permutate original...");
    time = System.currentTimeMillis();
    permute(inverse(indices), data, false);
    System.out.println(System.currentTimeMillis()-time + "ms");
    for (int i = 1; i < n; i++)
      if (cmp.compare(data[i-1], data[i]) > 0)
        System.err.println("proplem: permute original at " + i);
  }
}