A map of values by class. : Map « Collections Data Structure « Java






A map of values by class.

      
/*
 * SubclassMap.java Created Jul 9, 2009 by Andrew Butler, PSL
 */
//package prisms.util;

import java.lang.reflect.Array;

/**
 * A map of values by class. This differs significantly from a normal key-value
 * map in that a value may be returned for a class input if an entry exists for
 * one of the class's superclasses (or inherited interfaces); an entry is not
 * required for the class itself.
 * 
 * @param <C>
 *            The type whose subtypes are used as keys in this map
 * @param <V>
 *            The type of value stored in this map
 */
public class SubClassMap<C, V> {
  class MapItem {
    final Class<? extends C> theClass;

    V theValue;

    MapItem(Class<? extends C> clazz, V value) {
      theClass = clazz;
      theValue = value;
    }
  }

  private MapItem[] theItems;

  /**
   * Creates a SubClassMap
   */
  public SubClassMap() {
    theItems = new SubClassMap.MapItem[0];
  }

  /**
   * Adds or sets a value in this map
   * 
   * @param clazz
   *            The class to set the value for. After this, calls to
   *            {@link #get(Class)} with an argument that is
   *            <code>clazz</code> or one of its subclasses will return
   *            <code>value</code> unless there is another entry in the map
   *            for a subtype of <code>clazz</code> that is also a supertype
   *            of the argument to get(Class).
   * @param value
   *            The value to store in the map
   */
  public void put(Class<? extends C> clazz, V value) {
    for (MapItem item : theItems)
      if (item.theClass == clazz) {
        item.theValue = value;
        return;
      }
    theItems = ArrayUtils.add(theItems, new MapItem(clazz, value));
  }

  /**
   * Removes a value from this map
   * 
   * @param clazz
   *            The class to remove the value for. After this. calls to
   *            {@link #get(Class)} with an argument that is
   *            <code>clazz</code> or one of its subclasses will return the
   *            value associated with <code>clazz</code>'s nearest superclass
   *            unless there is another entry in the map for a subtype of
   *            <code>clazz</code> that is also a supertype of the argument to
   *            get(Class).
   * @param allDescending
   *            Whether all subclasses of <code>clazz</code> should be removed
   *            as well
   */
  public void remove(Class<? extends C> clazz, boolean allDescending) {
    for (int i = 0; i < theItems.length; i++) {
      if (theItems[i].theClass == clazz
          || (allDescending && clazz
              .isAssignableFrom(theItems[i].theClass)))
        theItems = ArrayUtils.remove(theItems, i);
    }
  }

  /**
   * Gets the value assigned to <code>subClass</code> or its nearest supertype
   * for which there is an entry in this map.
   * 
   * @param subClass
   *            The type to get the value for
   * @return The value associated with the given type or its nearest
   *         superclass for which this map has an entry
   */
  public V get(Class<? extends C> subClass) {
    MapItem[] matches = new SubClassMap.MapItem[0];
    for (MapItem item : theItems) {
      if (item.theClass == subClass)
        return item.theValue;
      else if (item.theClass.isAssignableFrom(subClass))
        matches = ArrayUtils.add(matches, item);
    }
    if (matches.length == 0)
      return null;
    int minDist = 0;
    MapItem match = null;
    for (MapItem item : matches) {
      int dist = getTypeDistance(item.theClass, subClass);
      if (match == null || dist < minDist) {
        minDist = dist;
        match = item;
      }
    }
    return match.theValue;
  }

  private static int getTypeDistance(Class<?> clazz, Class<?> subClass) {
    if (clazz == subClass)
      return 0;
    int dist = getTypeDistance(clazz, subClass.getSuperclass());
    if (dist >= 0)
      return dist + 1;
    for (Class<?> intf : subClass.getInterfaces()) {
      dist = getTypeDistance(clazz, intf);
      if (dist >= 0)
        return dist + 1;
    }
    return -1;
  }
}

/**
 * ArrayUtils provides some static methods for manipulating arrays easily when
 * using a tool such as {@link java.util.ArrayList} is inconvenient.
 */
final class ArrayUtils {
  /**
   * Gets the first element in the given array for which the
   * {@link EqualsChecker#equals(Object, Object)} returns true, or null if
   * this never occurs. The equals method is called with the element as the
   * first argument and the test parameter as the second.
   * 
   * @param <T>
   *            The type of array to get an element of
   * @param array
   *            The array to get the element of
   * @param test
   *            The object to compare the others with. This may be null.
   * @param checker
   *            The checker to determine which element to get
   * @return The first element in the array for which the
   *         {@link EqualsChecker#equals(Object, Object)} returns true, or
   *         null if this never occurs
   */
  public static <T> T get(T[] array, Object test, EqualsChecker checker) {
    for (T el : array)
      if (checker.equals(el, test))
        return el;
    return null;
  }

  /**
   * Inserts an element into the array--for primitive types
   * 
   * @param anArray
   *            The array to insert into
   * @param anElement
   *            The element to insert
   * @param anIndex
   *            The index for the new element
   * @return The new array with all elements of <code>anArray</code>, but with
   *         <code>anElement</code> inserted at index <code>anIndex</code>
   */
  public static Object addP(Object anArray, Object anElement, int anIndex) {
    Object ret;
    int length;
    if (anArray == null) {
      if (anIndex != 0)
        throw new ArrayIndexOutOfBoundsException("Cannot set "
            + anIndex + " element in a null array");
      ret = Array.newInstance(anElement.getClass(), 1);
      Array.set(ret, 0, anElement);
      return ret;
    } else {
      length = Array.getLength(anArray);
      ret = Array.newInstance(anArray.getClass().getComponentType(),
          length + 1);
    }
    System.arraycopy(anArray, 0, ret, 0, anIndex);
    put(ret, anElement, anIndex);
    System.arraycopy(anArray, anIndex, ret, anIndex + 1, length - anIndex);
    return ret;
  }

  /**
   * Inserts an element into the array
   * 
   * @param <T>
   *            The type of the object array
   * @param anArray
   *            The array to insert into
   * @param anElement
   *            The element to insert
   * @param anIndex
   *            The index for the new element
   * @return The new array with all elements of <code>anArray</code>, but with
   *         <code>anElement</code> inserted at index <code>anIndex</code>
   */
  public static <T> T[] add(T[] anArray, T anElement, int anIndex) {
    T[] ret;
    if (anArray == null) {
      if (anIndex != 0)
        throw new ArrayIndexOutOfBoundsException("Cannot set "
            + anIndex + " element in a null array");
      ret = (T[]) Array.newInstance(anElement.getClass(), 1);
      ret[0] = anElement;
      return ret;
    } else
      ret = (T[]) Array.newInstance(
          anArray.getClass().getComponentType(), anArray.length + 1);
    System.arraycopy(anArray, 0, ret, 0, anIndex);
    put(ret, anElement, anIndex);
    System.arraycopy(anArray, anIndex, ret, anIndex + 1, anArray.length
        - anIndex);
    return ret;
  }

  /**
   * @param anArray
   *            The array to extend
   * @param anElement
   *            The element to add into <code>anArray</code>
   * @return A new array of length <code>anArray.length+1</code> whose
   *         contents are those of <code>anArray</code> followed by
   *         <code>anElement</code>
   */
  public static Object addP(Object anArray, Object anElement) {
    int len = anArray == null ? 0 : Array.getLength(anArray);
    return addP(anArray, anElement, len);
  }

  /**
   * @param <T>
   *            The type of the array
   * @param anArray
   *            The array to extend
   * @param anElement
   *            The element to add into <code>anArray</code>
   * @return A new array of length <code>anArray.length+1</code> whose
   *         contents are those of <code>anArray</code> followed by
   *         <code>anElement</code>
   */
  public static <T> T[] add(T[] anArray, T anElement) {
    int len = anArray == null ? 0 : Array.getLength(anArray);
    return add(anArray, anElement, len);
  }

  /**
   * Moves an element in an array from one index to another
   * 
   * @param <T>
   *            The type of the array
   * @param anArray
   *            The array to move an element within
   * @param from
   *            The index of the element to move
   * @param to
   *            The index to move the element to
   * @return The original array
   */
  public static <T> T[] move(T[] anArray, int from, int to) {
    final T element = anArray[from];
    for (; from < to; from++)
      anArray[from] = anArray[from + 1];
    for (; from > to; from--)
      anArray[from] = anArray[from - 1];
    anArray[to] = element;
    return anArray;
  }

  /**
   * Moves an element in a primitive array from one index to another
   * 
   * @param anArray
   *            The array to move an element within
   * @param from
   *            The index of the element to move
   * @param to
   *            The index to move the element to
   * @return The original array
   */
  public static Object moveP(Object anArray, int from, int to) {
    if (anArray instanceof Object[])
      return move((Object[]) anArray, from, to);
    final Object element = Array.get(anArray, from);
    for (; from < to; from++)
      Array.set(anArray, from, Array.get(anArray, from + 1));
    for (; from > to; from--)
      Array.set(anArray, from, Array.get(anArray, from - 1));
    Array.set(anArray, to, element);
    return anArray;
  }

  private static void put(Object array, Object element, int index) {
    try {
      if (array instanceof Object[]) {
        try {
          ((Object[]) array)[index] = element;
        } catch (ArrayStoreException e) {
          throw new IllegalArgumentException(e.getMessage()
              + ": "
              + (element == null ? "null" : element.getClass()
                  .getName()) + " into "
              + array.getClass().getName());
        }
      } else {
        try {
          Array.set(array, index, element);
        } catch (IllegalArgumentException e) {
          throw new IllegalArgumentException(e.getMessage()
              + ": "
              + (element == null ? "null" : element.getClass()
                  .getName()) + " into "
              + array.getClass().getName(), e);
        }
      }
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new ArrayIndexOutOfBoundsException(index + " into "
          + Array.getLength(array));
    }
  }

  /**
   * Merges all elements of a set of arrays into a single array with no
   * duplicates. For primitive types.
   * 
   * @param type
   *            The type of the result
   * @param arrays
   *            The arrays to merge
   * @return A new array containing all elements of <code>array1</code> and
   *         all elements of <code>array2</code> that are not present in
   *         <code>array1</code>
   * @throws NullPointerException
   *             If either array is null
   * @throws ArrayStoreException
   *             If elements in the arrays are incompatible with
   *             <code>type</code>
   */
  public static Object mergeInclusiveP(Class<?> type, Object... arrays) {
    java.util.LinkedHashSet<Object> set = new java.util.LinkedHashSet<Object>();
    int i, j;
    for (i = 0; i < arrays.length; i++) {
      int len = Array.getLength(arrays[i]);
      for (j = 0; j < len; j++)
        set.add(Array.get(arrays[i], j));
    }
    Object ret = Array.newInstance(type, set.size());
    i = 0;
    for (Object el : set) {
      put(ret, el, i);
      i++;
    }
    return ret;
  }

  /**
   * Merges all elements of a set of arrays into a single array with no
   * duplicates.
   * 
   * @param <T1>
   *            The type of the result
   * @param <T2>
   *            The type of the input arrays
   * @param type
   *            The type of the result
   * @param arrays
   *            The arrays to merge
   * @return A new array containing all elements of <code>array1</code> and
   *         all elements of <code>array2</code> that are not present in
   *         <code>array1</code>
   * @throws NullPointerException
   *             If either array is null
   */
  public static <T1, T2 extends T1> T1[] mergeInclusive(Class<T1> type,
      T2[]... arrays) {
    java.util.LinkedHashSet<T1> set = new java.util.LinkedHashSet<T1>();
    int i, j;
    for (i = 0; i < arrays.length; i++) {
      for (j = 0; j < arrays[i].length; j++)
        set.add(arrays[i][j]);
    }
    return set.toArray((T1[]) Array.newInstance(type, set.size()));
  }

  /**
   * Merges elements found in each of a set of arrays into a single array with
   * no duplicates. For primitive types.
   * 
   * @param type
   *            The type of the result
   * @param arrays
   *            The arrays to merge
   * @return A new array containing all common elements between
   *         <code>array1</code> and <code>array2</code>
   * @throws NullPointerException
   *             If either array is null
   * @throws ArrayStoreException
   *             If elements in the arrays are incompatible with
   *             <code>type</code>
   */
  public static Object mergeExclusiveP(Class<?> type, Object... arrays) {
    if (arrays.length == 0)
      return Array.newInstance(type, 0);
    java.util.ArrayList<Object> retSet = new java.util.ArrayList<Object>();
    int i, j, k;
    int len = Array.getLength(arrays[0]);
    for (j = 0; j < len; j++)
      retSet.add(Array.get(arrays[0], j));
    for (i = 1; i < arrays.length; i++) {
      for (j = 0; j < retSet.size(); j++) {
        len = Array.getLength(arrays[i]);
        boolean hasEl = false;
        for (k = 0; k < len; k++)
          if (equalsUnordered(retSet.get(j), Array.get(arrays[i], k))) {
            hasEl = true;
            break;
          }
        if (!hasEl) {
          retSet.remove(j);
          j--;
        }
      }
    }
    Object ret = Array.newInstance(type, retSet.size());
    for (i = 0; i < retSet.size(); i++)
      Array.set(ret, i, retSet.get(i));
    return ret;
  }

  /**
   * Merges elements found in both of two arrays into a single array with no
   * duplicates.
   * 
   * @param <T1>
   *            The type of the result
   * @param <T2>
   *            The type of the input arrays
   * @param type
   *            The type of the result
   * @param arrays
   *            The arrays to merge
   * @return A new array containing all common elements between
   *         <code>array1</code> and <code>array2</code>
   * @throws NullPointerException
   *             If either array is null
   */
  public static <T1, T2 extends T1> T1[] mergeExclusive(Class<T1> type,
      T2[]... arrays) {
    if (arrays.length == 0)
      return (T1[]) Array.newInstance(type, 0);
    java.util.ArrayList<Object> retSet = new java.util.ArrayList<Object>();
    int i, j, k;
    for (j = 0; j < arrays[0].length; j++)
      retSet.add(arrays[0][j]);
    for (i = 1; i < arrays.length; i++) {
      for (j = 0; j < retSet.size(); j++) {
        boolean hasEl = false;
        for (k = 0; k < arrays[i].length; k++)
          if (equalsUnordered(retSet.get(j), arrays[i][k])) {
            hasEl = true;
            break;
          }
        if (!hasEl) {
          retSet.remove(j);
          j--;
        }
      }
    }
    return retSet.toArray((T1[]) Array.newInstance(type, retSet.size()));
  }

  /**
   * Concatenates a series of arrays of any type
   * 
   * @param type
   *            The type of the arrays
   * @param arrays
   *            The arrays to concatenate
   * @return An array containing all elements of the arrays
   */
  public static Object concatP(Class<?> type, Object... arrays) {
    if (arrays.length == 0)
      return Array.newInstance(type, 0);
    if (arrays.length == 1)
      return arrays[0];
    int size = 0;
    int[] sizes = new int[arrays.length];
    for (int a = 0; a < arrays.length; a++) {
      sizes[a] = Array.getLength(arrays[a]);
      size += sizes[a];
    }
    Object ret = Array.newInstance(type, size);
    int aLen = 0;
    for (int a = 0; a < arrays.length; a++) {
      System.arraycopy(arrays[a], 0, ret, aLen, sizes[a]);
      aLen += sizes[a];
    }
    return ret;
  }

  /**
   * Concatenates a series of arrays of a non-primitive type
   * 
   * @param <T>
   *            The type of the arrays
   * @param type
   *            The type of the arrays
   * @param arrays
   *            The arrays to concatenate
   * @return An array containing all elements of the arrays
   */
  public static <T> T[] concat(Class<T> type, T[]... arrays) {
    if (arrays.length == 0)
      return (T[]) Array.newInstance(type, 0);
    if (arrays.length == 1)
      return arrays[0];
    int size = 0;
    for (int a = 0; a < arrays.length; a++) {
      if (arrays[a] != null)
        size += arrays[a].length;
    }
    T[] ret = (T[]) Array.newInstance(type, size);
    int aLen = 0;
    for (int a = 0; a < arrays.length; a++) {
      if (arrays[a] == null)
        continue;
      System.arraycopy(arrays[a], 0, ret, aLen, arrays[a].length);
      aLen += arrays[a].length;
    }
    return ret;
  }

  /**
   * @param <T>
   *            The type of the array to search
   * @param anArray
   *            The array to search
   * @param anElement
   *            The element to search for
   * @return The first index <code>0&lt;=idx&lt;anArray.length</code> such
   *         that {@link #equals(Object, Object)} returns true for both
   *         <code>anArray[idx]</code> and <code>anElement</code>, or -1 if no
   *         such index exists
   */
  public static <T> int indexOf(T[] anArray, T anElement) {
    if (anArray == null)
      return -1;
    for (int i = 0; i < anArray.length; i++)
      if (equals(anArray[i], anElement))
        return i;
    return -1;
  }

  /**
   * @param anArray
   *            The array to search
   * @param anElement
   *            The element to search for
   * @return The first index <code>0&lt;=idx&lt;anArray.length</code> such
   *         that {@link #equals(Object, Object)} returns true for both
   *         <code>anArray[idx]</code> and <code>anElement</code>, or -1 if no
   *         such index exists
   */
  public static int indexOfP(Object anArray, Object anElement) {
    if (anArray == null)
      return -1;
    if (anArray instanceof Object[]) {
      Object[] array2 = (Object[]) anArray;
      for (int i = 0; i < array2.length; i++) {
        if (equals(array2[i], anElement))
          return i;
      }
      return -1;
    } else {
      int i, len;
      len = Array.getLength(anArray);
      for (i = 0; i < len; i++) {
        if (equals(Array.get(anArray, i), anElement))
          return i;
      }
      return -1;
    }
  }

  /**
   * Removes the specified object from the array. For primitive types.
   * 
   * @param anArray
   *            The array to remove an element from
   * @param anIndex
   *            The index of the element to remove
   * @return A new array with all the elements of <code>anArray</code> except
   *         the element at <code>anIndex</code>
   */
  public static Object removeP(Object anArray, int anIndex) {
    Object ret;
    int length;
    if (anArray == null)
      return null;
    else {
      length = Array.getLength(anArray);
      ret = Array.newInstance(anArray.getClass().getComponentType(),
          length - 1);
    }
    System.arraycopy(anArray, 0, ret, 0, anIndex);
    System.arraycopy(anArray, anIndex + 1, ret, anIndex, length - anIndex
        - 1);
    return ret;
  }

  /**
   * Removes the specified object from the array
   * 
   * @param <T>
   *            The type of the array
   * @param anArray
   *            The array to remove an element from
   * @param anIndex
   *            The index of the element to remove
   * @return A new array with all the elements of <code>anArray</code> except
   *         the element at <code>anIndex</code>
   */
  public static <T> T[] remove(T[] anArray, int anIndex) {
    T[] ret;
    if (anArray == null)
      return null;
    else {
      ret = (T[]) Array.newInstance(
          anArray.getClass().getComponentType(), anArray.length - 1);
    }
    System.arraycopy(anArray, 0, ret, 0, anIndex);
    System.arraycopy(anArray, anIndex + 1, ret, anIndex, anArray.length
        - anIndex - 1);
    return ret;
  }

  /**
   * Removes the specified object from the array
   * 
   * @param anArray
   *            The array to remove an element from
   * @param anElement
   *            The element to remove
   * @return A new array with all the elements of <code>anArray</code> except
   *         <code>anElement</code>
   */
  public static Object removeP(Object anArray, Object anElement) {
    int idx = indexOfP(anArray, anElement);
    if (idx >= 0)
      return removeP(anArray, idx);
    else
      return anArray;
  }

  /**
   * Removes the specified object from the array
   * 
   * @param <T>
   *            The type of the array
   * @param anArray
   *            The array to remove an element from
   * @param anElement
   *            The element to remove
   * @return A new array with all the elements of <code>anArray</code> except
   *         <code>anElement</code>
   */
  public static <T> T[] remove(T[] anArray, T anElement) {
    int idx = indexOf(anArray, anElement);
    if (idx >= 0)
      return remove(anArray, idx);
    else
      return anArray;
  }

  /**
   * Removes all contents of <code>array2</code> from <code>array1</code>. All
   * instances of <code>array2</code> will also be removed from
   * <code>array1</code>. For primitive types.
   * 
   * @param array1
   *            The array to remove elements from
   * @param array2
   *            The array containing the elements to remove; or the element to
   *            remove itself
   * @return <code>array1</code> missing all the contents of
   *         <code>array2</code>
   */
  public static Object removeAllP(Object array1, Object array2) {
    if (array1 == null || array2 == null)
      return array1;
    if (!array1.getClass().isArray())
      return null;
    if (!array2.getClass().isArray())
      array2 = new Object[] { array2 };
    else
      array2 = addP(array2, array2);
    java.util.BitSet remove = new java.util.BitSet();
    int len1 = Array.getLength(array1);
    int len2 = Array.getLength(array2);
    int i, j;
    for (i = 0; i < len1; i++) {
      for (j = 0; j < len2; j++) {
        if (equals(Array.get(array1, i), Array.get(array2, j))) {
          remove.set(i);
          break;
        }
      }
    }
    Object ret = Array.newInstance(array1.getClass().getComponentType(),
        len1 - remove.cardinality());
    // This copying section might be replaced by a more efficient version
    // using System.arraycopy()--this would be much faster than reflection,
    // especially for large arrays needing only a few elements removed
    for (i = 0, j = 0; i < len1; i++) {
      if (!remove.get(i)) {
        put(ret, Array.get(array1, i), j);
        j++;
      }
    }
    return ret;
  }

  /**
   * Removes all contents of <code>array2</code> from <code>array1</code>. All
   * instances of <code>array2</code> will also be removed from
   * <code>array1</code>.
   * 
   * @param <T>
   *            The type of the array
   * @param array1
   *            The array to remove elements from
   * @param array2
   *            The array containing the elements to remove; or the element to
   *            remove itself
   * @return <code>array1</code> missing all the contents of
   *         <code>array2</code>
   */
  public static <T> T[] removeAll(T[] array1, Object array2) {
    if (array1 == null || array2 == null)
      return array1;
    if (!array1.getClass().isArray())
      return null;
    if (!array2.getClass().isArray())
      array2 = new Object[] { array2 };
    java.util.BitSet remove = new java.util.BitSet();
    int len1 = array1.length;
    int len2 = Array.getLength(array2);
    int i, j;
    for (i = 0; i < len1; i++) {
      for (j = 0; j < len2; j++) {
        if (equals(array1[i], Array.get(array2, j))) {
          remove.set(i);
          break;
        }
      }
    }
    T[] ret = (T[]) Array.newInstance(array1.getClass().getComponentType(),
        len1 - remove.cardinality());
    // This copying section might be replaced by a more efficient version
    // using System.arraycopy()--this would be much faster than reflection,
    // especially for large arrays needing only a few elements removed
    for (i = 0, j = 0; i < len1; i++) {
      if (!remove.get(i)) {
        ret[j] = array1[i];
        j++;
      }
    }
    return ret;
  }

  /**
   * Searches an array (possibly primitive) for an element
   * 
   * @param anArray
   *            The array to search
   * @param anElement
   *            The element to search for
   * @return True if <code>anArray</code> contains <code>anElement</code>,
   *         false otherwise
   */
  public static boolean containsP(Object anArray, Object anElement) {
    if (anArray == null)
      return false;
    if (anArray instanceof Object[]) {
      Object[] oa = (Object[]) anArray;
      for (int i = 0; i < oa.length; i++) {
        if (equals(oa[i], anElement))
          return true;
      }
    } else {
      int len = Array.getLength(anArray);
      for (int i = 0; i < len; i++) {
        if (equals(Array.get(anArray, i), anElement))
          return true;
      }
    }
    return false;
  }

  /**
   * @param <T>
   *            The type of the array to search
   * @param anArray
   *            The array to search
   * @param anElement
   *            The element to search for
   * @return True if <code>anArray</code> contains <code>anElement</code>,
   *         false otherwise
   */
  public static <T> boolean contains(T[] anArray, T anElement) {
    if (anArray == null)
      return false;
    for (int i = 0; i < anArray.length; i++) {
      if (equals(anArray[i], anElement))
        return true;
    }
    return false;
  }

  /**
   * Like {@link #containsP(Object, Object)} but the equality test is by
   * identity instead of the equals method
   * 
   * @param anArray
   *            The array to search
   * @param anElement
   *            The element to search for
   * @return True if <code>anArray</code> contains <code>anElement</code> by
   *         identity, false otherwise
   */
  public static boolean containspID(Object anArray, Object anElement) {
    if (anArray == null)
      return false;
    if (anArray instanceof Object[]) {
      Object[] oa = (Object[]) anArray;
      for (int i = 0; i < oa.length; i++) {
        if (oa[i] == anElement)
          return true;
      }
    } else {
      int len = Array.getLength(anArray);
      for (int i = 0; i < len; i++) {
        if (Array.get(anArray, i) == anElement)
          return true;
      }
    }
    return false;
  }

  /**
   * Like {@link #contains(Object[], Object)} but the equality test is by
   * identity instead of the equals method
   * 
   * @param <T>
   *            The type of the array to search
   * @param <E>
   *            The type of the element to search for
   * @param anArray
   *            The array to search
   * @param anElement
   *            The element to search for
   * @return True if <code>anArray</code> contains <code>anElement</code> by
   *         identity, false otherwise
   */
  public static <T, E extends T> boolean containsID(T[] anArray, E anElement) {
    if (anArray == null)
      return false;
    for (int i = 0; i < anArray.length; i++) {
      if (anArray[i] == anElement)
        return true;
    }
    return false;
  }

  /**
   * A utility version of equals that allows comparison of null objects and
   * arrays. WARNING: Does not account for an array that contains a reference
   * to itself, directly or indirectly
   * 
   * @param o1
   *            The first object to compare
   * @param o2
   *            The second object to compare
   * @return true if <code>o1</code> and <code>o2</code> are arrays and their
   *         elements are equivalent or if either
   *         <code>o1==null && o2==null</code> or <code>o1.equals(o2)</code>,
   *         false otherwise
   */
  public static boolean equals(Object o1, Object o2) {
    if (o1 == null)
      return o2 == null;
    if (o2 == null)
      return false;
    if (o1 instanceof Object[] && o2 instanceof Object[])
      return equals((Object[]) o1, (Object[]) o2);
    if (o1.getClass().isArray() && o2.getClass().isArray()) {
      if (!o1.getClass().equals(o2.getClass()))
        return false;
      int len = Array.getLength(o1);
      if (len != Array.getLength(o2))
        return false;
      for (int i = 0; i < len; i++) {
        if (!equals(Array.get(o1, i), Array.get(o2, i)))
          return false;
      }
      return true;
    }
    return o1.equals(o2);
  }

  private static boolean equals(Object[] o1, Object[] o2) {
    if (o1 == null)
      return o2 == null;
    if (o2 == null)
      return false;
    if (!o1.getClass().equals(o2.getClass()))
      return false;
    if (o1.length != o2.length)
      return false;
    for (int i = 0; i < o1.length; i++) {
      if (!equals(o1[i], o2[i]))
        return false;
    }
    return true;
  }

  /**
   * Like {@link #equals(Object, Object)}, but compares arrays without regard
   * to order
   * 
   * @param o1
   *            The first object to compare
   * @param o2
   *            The second object to compare
   * @return true if <code>o1</code> and <code>o2</code> are arrays and their
   *         elements are equivalent or if either
   *         <code>o1==null && o2==null</code> or <code>o1.equals(o2)</code>,
   *         false otherwise
   */
  public static boolean equalsUnordered(Object o1, Object o2) {
    return equalsUnordered(o1, o2, new EqualsChecker() {
      public boolean equals(Object o3, Object o4) {
        if (o3 == null)
          return o4 == null;
        if (o4 == null)
          return false;
        if (!o3.getClass().isArray()) {
          if (o4.getClass().isArray())
            return false;
          return o3.equals(o4);
        } else if (!o4.getClass().isArray())
          return false;
        return equalsUnordered(o3, o4);
      }
    });
  }

  /**
   * Checks two values for equality
   */
  public static interface EqualsChecker {
    /**
     * Checks two values for equality
     * 
     * @param o1
     *            The first value to check
     * @param o2
     *            The second value to check
     * @return Whether the two values are equal
     */
    boolean equals(Object o1, Object o2);
  }

  /**
   * Like {@link #equals(Object, Object)}, but compares arrays without regard
   * to order and uses an independent checker to check for equality of
   * elements
   * 
   * @param o1
   *            The first object to compare
   * @param o2
   *            The second object to compare
   * @param checker
   *            The checker to check elements for equality
   * @return true if <code>o1</code> and <code>o2</code> are arrays and their
   *         elements are equivalent or if either
   *         <code>o1==null && o2==null</code> or <code>o1.equals(o2)</code>,
   *         false otherwise
   */
  public static boolean equalsUnordered(Object o1, Object o2,
      EqualsChecker checker) {
    if (o1 == null)
      return o2 == null;
    if (o2 == null)
      return false;
    if (o1 instanceof Object[] && o2 instanceof Object[])
      return equalsUnordered((Object[]) o1, (Object[]) o2, checker);
    if (o1.getClass().isArray() && o2.getClass().isArray()) {
      if (!o1.getClass().equals(o2.getClass()))
        return false;
      int len = Array.getLength(o1);
      if (len != Array.getLength(o2))
        return false;
      if (len == 0)
        return true;
      long[] bs = new long[(len - 1) / 64 + 1];
      long mask;
      int i, j, bsIdx;
      o1Loop: for (i = 0; i < len; i++) {
        mask = 0;
        for (j = 0; j < len; j++) {
          bsIdx = j / 64;
          if (mask == 0)
            mask = 0x8000000000000000L;
          if ((bs[bsIdx] & mask) == 0
              && checker.equals(Array.get(o1, i),
                  Array.get(o2, j))) {
            bs[bsIdx] |= mask;
            continue o1Loop;
          }
          mask >>>= 1;
        }
        return false;
      }
      return true;
    }
    return checker.equals(o1, o2);
  }

  private static boolean equalsUnordered(Object[] o1, Object[] o2,
      EqualsChecker checker) {
    if (o1 == null)
      return o2 == null;
    if (o2 == null)
      return false;
    if (o1.length != o2.length)
      return false;
    if (o1.length == 0)
      return true;
    long[] bs = new long[(o2.length - 1) / 64 + 1];
    long mask;
    int i, j, bsIdx;
    o1Loop: for (i = 0; i < o1.length; i++) {
      mask = 0;
      for (j = 0; j < o2.length; j++) {
        bsIdx = j / 64;
        if (mask == 0)
          mask = 0x8000000000000000L;
        if ((bs[bsIdx] & mask) == 0 && checker.equals(o1[i], o2[j])) {
          bs[bsIdx] |= mask;
          continue o1Loop;
        }
        mask >>>= 1;
      }
      return false;
    }
    return true;
  }

  /**
   * A compliment to the {@link #equals(Object, Object)} method, this method
   * returns a hashcode for <code>array</code> such that it is consistent with
   * the equals contract for the {@link Object#equals(Object)} method,
   * represented by the {@link #equals(Object, Object)} method.
   * 
   * @param array
   *            The array to get a hashcode for
   * @return A hashcode based on <code>array</code> and its contents, if any
   */
  public static int hashCode(Object array) {
    if (array == null)
      return 0;
    if (!array.getClass().isArray())
      return array.hashCode();
    int ret = array.getClass().getComponentType().hashCode();
    int len = Array.getLength(array);
    for (int i = 0; i < len; i++) {
      ret *= hashCode(Array.get(array, i));
      ret += 29;
    }
    return ret;
  }

  /**
   * A utility method for printing out the contents of an array (not deeply)
   * 
   * @param array
   *            The array to print
   * @return A String containing the representation of the contents of
   *         <code>array</code>
   */
  public static String toString(Object array) {
    if (array == null)
      return "" + null;
    else if (array instanceof Object[]) {
      Object[] oa = (Object[]) array;
      StringBuffer ret = new StringBuffer("[");
      for (int i = 0; i < oa.length; i++) {
        ret.append(toString(oa[i]));
        if (i < oa.length - 1)
          ret.append(", ");
      }
      ret.append("]");
      return ret.toString();
    } else if (array.getClass().isArray()) {
      StringBuffer ret = new StringBuffer("[");
      int len = Array.getLength(array);
      for (int i = 0; i < len; i++) {
        ret.append(Array.get(array, i));
        if (i < len - 1)
          ret.append(", ");
      }
      ret.append("]");
      return ret.toString();
    } else
      return array.toString();
  }

  /**
   * Replaces <code>toReplace</code> with <code>replacement</code> in
   * <code>array</code> one time at the most
   * 
   * @param array
   *            The array to search and replace in
   * @param toReplace
   *            The object to replace
   * @param replacement
   *            The object to replace <code>toReplace</code> with the first
   *            time it is found
   * @return The index where <code>toReplace</code> was found and replaced, or
   *         -1 if it was not found in <code>array</code>
   */
  public static int replaceOnce(Object array, Object toReplace,
      Object replacement) {
    if (array == null)
      return -1;
    if (array instanceof Object[]) {
      Object[] array2 = (Object[]) array;
      for (int i = 0; i < array2.length; i++) {
        if (equals(array2[i], toReplace)) {
          array2[i] = replacement;
          return i;
        }
      }
      return -1;
    } else {
      int i, len;
      len = Array.getLength(array);
      for (i = 0; i < len; i++) {
        if (equals(Array.get(array, i), toReplace)) {
          put(array, replacement, i);
          return i;
        }
      }
      return -1;
    }
  }

  /**
   * Replaces <code>toReplace</code> with <code>replacement</code> in
   * <code>array</code> as many times as it occurs
   * 
   * @param array
   *            The array to search and replace in
   * @param toReplace
   *            The object to replace
   * @param replacement
   *            The object to replace <code>toReplace</code> with each time it
   *            is found
   * @return The number of times <code>toReplace</code> was found and replaced
   */
  public static int replaceAll(Object array, Object toReplace,
      Object replacement) {
    int count = 0;
    if (array == null)
      return count;
    if (array instanceof Object[]) {
      Object[] array2 = (Object[]) array;
      for (int i = 0; i < array2.length; i++) {
        if (equals(array2[i], toReplace)) {
          array2[i] = replacement;
          count++;
        }
      }
    } else {
      int i, len;
      len = Array.getLength(array);
      for (i = 0; i < len; i++) {
        if (equals(Array.get(array, i), toReplace)) {
          put(array, replacement, i);
          count++;
        }
      }
    }
    return count;
  }

  /**
   * Gets the elements that the two arrays have in common. Elements are
   * retrieved from the first array.
   * 
   * @param <T>
   *            The type of the first array and the return array
   * @param <T2>
   *            The type of the second array
   * @param array1
   *            The first array
   * @param array2
   *            The second array
   * @return The elements in <code>array1</code> that occur at least once in
   *         <code>array2</code>
   */
  public static <T, T2 extends T> T[] commonElements(T[] array1, T2[] array2) {
    int count = 0;
    if (array1 == null || array2 == null)
      return (T[]) Array.newInstance(
          array1.getClass().getComponentType(), 0);
    int i, j;
    for (i = 0; i < array1.length; i++)
      for (j = 0; j < array2.length; j++)
        if (equals(array1[i], array2[j])) {
          count++;
          break;
        }
    T[] ret = (T[]) Array.newInstance(array1.getClass().getComponentType(),
        count);
    count = 0;
    for (i = 0; i < array1.length; i++)
      for (j = 0; j < array2.length; j++)
        if (equals(array1[i], array2[j])) {
          ret[count] = array1[i];
          count++;
          break;
        }
    return ret;
  }

  /**
   * Gets the elements that the two arrays have in common. Elements are
   * retrieved from the first array.
   * 
   * @param array1
   *            The first array
   * @param array2
   *            The second array
   * @return The elements in <code>array1</code> that occur at least once in
   *         <code>array2</code>
   */
  public static Object[] commonElementsP(Object array1, Object array2) {
    int count = 0;
    if (array1 == null || array2 == null)
      return new Object[0];
    if (!array1.getClass().isArray() || array2.getClass().isArray())
      return new Object[0];
    int len1 = Array.getLength(array1);
    int len2 = Array.getLength(array2);
    int i, j;
    for (i = 0; i < len1; i++)
      for (j = 0; j < len2; j++)
        if (equals(Array.get(array1, i), Array.get(array2, j))) {
          count++;
          break;
        }
    Object[] ret = new Object[count];
    count = 0;
    for (i = 0; i < len1; i++)
      for (j = 0; j < len2; j++)
        if (equals(Array.get(array1, i), Array.get(array2, j))) {
          ret[count] = Array.get(array1, i);
          count++;
          break;
        }
    return ret;
  }

  /**
   * Gets the elements that are in array1, but not array 2.
   * 
   * @param <T>
   *            The type of the arrays
   * @param array1
   *            The first array
   * @param array2
   *            The second array
   * @return The elements in <code>array1</code> that do not occur in
   *         <code>array2</code>
   */
  public static <T> T[] removedElements(T[] array1, T[] array2) {
    int count = 0;
    if (array1 == null || array2 == null)
      return array1;
    int len1 = array1.length;
    int len2 = array2.length;
    int i, j;
    for (i = 0; i < len1; i++) {
      count++;
      for (j = 0; j < len2; j++) {
        if (equals(array1[i], array2[j])) {
          count--;
          break;
        }
      }
    }
    T[] ret = (T[]) Array.newInstance(array1.getClass().getComponentType(),
        count);
    count = 0;
    for (i = 0; i < len1; i++) {
      count++;
      for (j = 0; j < len2; j++) {
        if (equals(array1[i], array2[j])) {
          count--;
          break;
        }
        ret[count] = array1[i];
      }
    }
    return ret;
  }

  /**
   * Gets the elements that are in array1, but not array 2.
   * 
   * @param array1
   *            The first array
   * @param array2
   *            The second array
   * @return The elements in <code>array1</code> that do not occur in
   *         <code>array2</code>
   */
  public static Object[] removedElementsP(Object array1, Object array2) {
    int count = 0;
    if (array1 == null || array2 == null)
      return new Object[0];
    if (!array1.getClass().isArray() || array2.getClass().isArray())
      return new Object[0];
    int len1 = Array.getLength(array1);
    int len2 = Array.getLength(array2);
    int i, j;
    for (i = 0; i < len1; i++) {
      count++;
      for (j = 0; j < len2; j++) {
        if (equals(Array.get(array1, i), Array.get(array2, j))) {
          count--;
          break;
        }
      }
    }
    Object[] ret = new Object[count];
    count = 0;
    for (i = 0; i < len1; i++) {
      count++;
      for (j = 0; j < len2; j++) {
        if (equals(Array.get(array1, i), Array.get(array2, j))) {
          count--;
          break;
        }
        ret[count] = Array.get(array1, i);
      }
    }
    return ret;
  }

  /**
   * Gets the elements that are in array2, but not array 1.
   * 
   * @param array1
   *            The first array
   * @param array2
   *            The second array
   * @return The elements in <code>array2</code> that do not occur in
   *         <code>array1</code>
   */
  public static Object[] addedElements(Object array1, Object array2) {
    int count = 0;
    if (array1 == null || array2 == null)
      return new Object[0];
    if (!array1.getClass().isArray() || array2.getClass().isArray())
      return new Object[0];
    int len1 = Array.getLength(array1);
    int len2 = Array.getLength(array2);
    int i, j;
    for (i = 0; i < len1; i++) {
      count++;
      for (j = 0; j < len2; j++) {
        if (equals(Array.get(array2, i), Array.get(array1, j))) {
          count--;
          break;
        }
      }
    }
    Object[] ret = new Object[count];
    count = 0;
    for (i = 0; i < len1; i++) {
      count++;
      for (j = 0; j < len2; j++) {
        if (equals(Array.get(array2, i), Array.get(array1, j))) {
          count--;
          break;
        }
        ret[count] = Array.get(array2, i);
      }
    }
    return ret;
  }

  /**
   * Reverses the order of an array
   * 
   * @param <T>
   *            The type of the array to reverse
   * @param array
   *            The array to reverse
   * @return The reversed array, same as the original reference
   */
  public static <T> T[] reverse(T[] array) {
    if (array == null)
      return array;
    T temp;
    final int aLen = array.length - 1;
    for (int i = 0; i < array.length / 2; i++) {
      temp = array[i];
      array[i] = array[aLen - i];
      array[aLen - i] = temp;
    }
    return array;
  }

  /**
   * Reverses the order of an array. Similar to {@link #reverse(Object[])} but
   * works for primitive arrays as well.
   * 
   * @param array
   *            The array to reverse
   * @return The reversed array, same as the original reference
   */
  public static Object reverseP(Object array) {
    if (array == null)
      return array;
    if (array instanceof Object[])
      return reverse((Object[]) array);
    Object temp;
    final int aLen = Array.getLength(array);
    for (int i = 0; i < aLen / 2; i++) {
      temp = Array.get(array, i);
      put(array, Array.get(array, aLen - i - 1), i);
      put(array, temp, aLen - i - 1);
    }
    return array;
  }

  /**
   * @see DifferenceListenerE
   * 
   * @param <T1>
   *            The type of the original array
   * @param <T2>
   *            The type of the modifying array
   */
  public static interface DifferenceListener<T1, T2> extends
      DifferenceListenerE<T1, T2, RuntimeException> {
    /**
     * @see prisms.util.ArrayUtils.DifferenceListenerE#identity(java.lang.Object,
     *      java.lang.Object)
     */
    boolean identity(T1 o1, T2 o2);

    /**
     * @see prisms.util.ArrayUtils.DifferenceListenerE#added(java.lang.Object,
     *      int, int)
     */
    T1 added(T2 o, int mIdx, int retIdx);

    /**
     * @see prisms.util.ArrayUtils.DifferenceListenerE#removed(java.lang.Object,
     *      int, int, int)
     */
    T1 removed(T1 o, int oIdx, int incMod, int retIdx);

    /**
     * @see prisms.util.ArrayUtils.DifferenceListenerE#set(java.lang.Object,
     *      int, int, java.lang.Object, int, int)
     */
    T1 set(T1 o1, int idx1, int incMod, T2 o2, int idx2, int retIdx);
  }

  /**
   * <p>
   * This listener contains all information needed to reconcile two arrays
   * using the
   * {@link ArrayUtils#adjust(Object[], Object[], prisms.util.ArrayUtils.DifferenceListenerE)}
   * method.
   * </p>
   * 
   * <p>
   * There are many different indices that may or may not be relevant to a
   * particular listener:
   * <ul>
   * <li><b>oIdx:</b> The index of the element in the original array</li>
   * <li><b>incMod:</b> The index of the element in a copy of the original
   * array that has been modified incrementally for each add, remove, and
   * reorder operation</li>
   * <li><b>mIdx:</b> The index of the element in the modifier array</li>
   * <li><b>retIdx:</b> The index that the returned element (if not null) will
   * be at in the result array</li>
   * </ul>
   * oIdx and mIdx are self-explanatory. incMod and retIdx are useful for
   * listeners that modify an external ordered set incrementally with each
   * listener method invocation.
   * </p>
   * 
   * <p>
   * <b>An Example</b><br />
   * Suppose there is a list, <b>L</b>, whose data can only be modified
   * incrementally by the methods add(value, index), remove(index), and
   * move(fromIndex, toIndex), but can be accessed in batch by the getData
   * method, which returns an array of all data in the list. Suppose the list
   * needs to be modified to represent a new arbitrary set of data, <b>d</b>.
   * <b>d</b> may or may not contain elements represented in <b>L</b>. These
   * elements, if present, may be in different order and new items not present
   * in <b>L</b> may be present in <b>d</b>. To modify <b>L</b> to represent
   * the data in <b>d</b> in the correct order, the
   * {@link ArrayUtils#adjust(Object[], Object[], DifferenceListenerE)} method
   * should be called with <b>L</b>.getData(), <b>d</b>, and a listener whose
   * methods perform the following operations:
   * <ul>
   * <li>{@link #identity(Object, Object)}: Tests the two elements to see if
   * the item from <b>L</b> represents the item in <b>d</b>, possibly by
   * simply calling {@link Object#equals(Object)}</li>
   * <li>{@link #added(Object, int, int)}: Invokes <b>L</b>.add(o2, retIdx).</li>
   * <li>{@link #removed(Object, int, int, int)}: Invokes
   * <b>L</b>.remove(incMod). Using incMod accounts for any previous
   * modifications that may have been performed on the list from the listener.
   * </li>
   * <li>{@link #set(Object, int, int, Object, int, int)}: Invokes
   * <b>L</b>.move(incMod, retIdx). This again accounts for all previous
   * changes to the list and moves the element to the index intended by the
   * adjust method.</li>
   * </ul>
   * The end result of this invocation will be that <b>L</b> contains all the
   * data in <b>d</b>, only the data in <b>d</b>, and is ordered identically
   * to <b>d</b>. The returned array may be ignored.
   * </p>
   * 
   * <p>
   * <b>Alternate Operation:</b><br />
   * <ul>
   * <li>If it is intended that the modification operation should only add
   * data to <b>L</b>, the remove method may do nothing and return o. The
   * return value will cause the adjust method to update future indexes,
   * assuming that the value is kept and not removed.</li>
   * <li>If order is not important or no move(fromIndex, toIndex) method
   * exists, the set method may do nothing and return o1. This will have no
   * effect on the result of the operation other than leaving the items
   * originally in <b>L</b> (those that were also present in <b>d</b>) in
   * their original order.</li>
   * <li>Other permutations may exist depending on the business logic used by
   * the listener.</li>
   * </ul>
   * </p>
   * 
   * <p>
   * The listener is entirely free to choose whether items are added or
   * removed from the original array and its representations by modifying
   * whether the return value from those methods is null. The adjust method
   * does not care what is returned from the add/remove/set methods except
   * whether the value is null. The listener is free to perform index moving
   * operations or not. This method will never cause an index out of bounds
   * error assuming the related data sets are not modified by anything other
   * than the listener and the data in the arrays passed into the adjust
   * method do not change.
   * </p>
   * 
   * @param <T1>
   *            The type of the original array
   * @param <T2>
   *            The type of the modifying array
   * @param <E>
   *            The type of exception that may be thrown
   */
  public static interface DifferenceListenerE<T1, T2, E extends Throwable> {
    /**
     * Tests the identity of an item from each of the two sets. This method
     * should return true if one item is a representation of the other or
     * both are a representation of the same piece of data.
     * 
     * @param o1
     *            The object from the first set
     * @param o2
     *            The object from the second set
     * @return Whether the two objects are fundamentally equivalent
     * @throws E
     *             If an error occurs in this method
     */
    boolean identity(T1 o1, T2 o2) throws E;

    /**
     * Called when a value is found in the second set that is not present in
     * the first set
     * 
     * @param o
     *            The unmatched object
     * @param mIdx
     *            The index in the second array where the unmatched object
     *            was found
     * @param retIdx
     *            The index that the new element will be inserted into the
     *            final array
     * @return The new T1-type element to insert into the return array, or
     *         null if the new element is not to be inserted
     * @throws E
     *             If an error occurs in this method
     */
    T1 added(T2 o, int mIdx, int retIdx) throws E;

    /**
     * Called when a value is found in the first set that is not present in
     * the second set
     * 
     * @param o
     *            The unmatched object
     * @param oIdx
     *            The index in the first array where the unmatched object
     *            was found
     * @param incMod
     *            The index where the unmatched element would occur the
     *            incrementally modified original array
     * @param retIdx
     *            The index in the final array where the replacement will be
     *            located if a non-null value is returned
     * @return null if the original object is to be removed, otherwise its
     *         replacement
     * @throws E
     *             If an error occurs in this method
     */
    T1 removed(T1 o, int oIdx, int incMod, int retIdx) throws E;

    /**
     * Called when elements in the two arrays match
     * 
     * @param o1
     *            The element in the first array
     * @param idx1
     *            The index of the element in the first array
     * @param incMod
     *            The index where the element in the original array would
     *            occur the incrementally modified original array
     * @param o2
     *            The element in the second array
     * @param idx2
     *            The index of the element in the second array
     * @param retIdx
     *            The index of the returned element in the final array
     * @return The element to be inserted in the final array, or null if the
     *         element is to be removed
     * @throws E
     *             If an error occurs in this method
     */
    T1 set(T1 o1, int idx1, int incMod, T2 o2, int idx2, int retIdx)
        throws E;
  }

  /**
   * <p>
   * Reconciles differences between two ordered sets of objects. Allows a
   * programmer highly customized control between two different
   * representations of a data set. The arrays are compared and the listener
   * is notified when any differences are encountered, allowing it to
   * determine the composition of the returned array and/or perform
   * well-defined operations represented by the differences or similarities.
   * </p>
   * 
   * @param <T1>
   *            The type of the original array
   * @param <T2>
   *            The type of the modifying array
   * @param <E>
   *            The type of exception that may be thrown
   * @param original
   *            The original array
   * @param modifier
   *            The modifying array
   * @param dl
   *            The listener to determine how to deal with differences between
   *            the two arrays
   * @return A final array that is the result of applying select changes
   *         between the original and the modifying arrays
   * @throws E
   *             If the {@link DifferenceListenerE} throws an exception
   */
  public static <T1, T2, E extends Throwable> T1[] adjust(T1[] original,
      T2[] modifier, DifferenceListenerE<T1, T2, E> dl) throws E {
    ArrayAdjuster<T1, T2, E> adjuster = new ArrayAdjuster<T1, T2, E>(
        original, modifier, dl);
    return adjuster.adjust();
  }

  static final Object NULL = new Object();

  /**
   * Adjusts arrays. This is the more complicated and capable structure used
   * by {@link ArrayUtils#adjust(Object[], Object[], DifferenceListenerE)}
   * 
   * @param <T1>
   *            The type of the original array
   * @param <T2>
   *            The type of the modifying array
   * @param <E>
   *            The type of exception that may be thrown
   */
  public static class ArrayAdjuster<T1, T2, E extends Throwable> {
    private final T1[] original;

    private final int[] oIdxAdj;

    private final int[] oMappings;

    private final T2[] modifier;

    private final int[] mMappings;

    private final boolean[] entriesSet;

    private final DifferenceListenerE<T1, T2, E> dl;

    private int maxLength;

    private boolean isNullElement;

    /**
     * Creates an adjuster that can adjust one array by another
     * 
     * @param o
     *            The original array
     * @param m
     *            The modified array
     * @param listener
     *            The listener to determine how to deal with differences
     *            between the two arrays
     * @see ArrayUtils#adjust(Object[], Object[], DifferenceListenerE)
     */
    public ArrayAdjuster(T1[] o, T2[] m,
        DifferenceListenerE<T1, T2, E> listener) {
      original = o;
      oIdxAdj = new int[o.length];
      oMappings = new int[o.length];
      modifier = m;
      mMappings = new int[m.length];
      entriesSet = new boolean[m.length];
      dl = listener;
    }

    private void init() throws E {
      int o, m, r = original.length + modifier.length;
      for (m = 0; m < modifier.length; m++)
        mMappings[m] = -1;
      for (o = 0; o < original.length; o++) {
        oIdxAdj[o] = o;
        oMappings[o] = -1;
        for (m = 0; m < modifier.length; m++) {
          if (mMappings[m] >= 0)
            continue;
          if (dl.identity(original[o], modifier[m])) {
            oMappings[o] = m;
            mMappings[m] = o;
            r--;
            break;
          }
        }
      }
      maxLength = r;
    }

    /**
     * Adjusts the arrays set from the constructor
     * 
     * @return The adjusted array
     * @throws E
     *             If an error occurs in one of the listener's methods
     * @see ArrayUtils#adjust(Object[], Object[], DifferenceListenerE)
     */
    public T1[] adjust() throws E {
      init();
      int m, o, r = 0;
      Object[] ret = new Object[maxLength];
      for (o = 0; o < original.length && oMappings[o] < 0; o++)
        if (remove(o, -1, ret, r))
          r++;
      for (m = 0; m < modifier.length; m++) {
        // Add or set each modifier
        o = mMappings[m];
        if (o >= 0) {
          if (set(o, m, ret, r))
            r++;
          /* Remove the originals that occur before the next match */
          for (o++; o < original.length && oMappings[o] < 0; o++) {
            if (ret[r] != null) {
              Object temp = ret[r];
              for (int r2 = r; r < ret.length - 1 && temp != null; r2++) {
                Object temp2 = ret[r2 + 1];
                ret[r2 + 1] = temp;
                temp = temp2;
              }
            }
            if (remove(o, m + 1, ret, r))
              r++;
          }
        } else {
          if (ret[r] != null) {
            Object temp = ret[r];
            for (int r2 = r; r < ret.length - 1 && temp != null; r2++) {
              Object temp2 = ret[r2 + 1];
              ret[r2 + 1] = temp;
              temp = temp2;
            }
          }
          if (add(m, ret, r))
            r++;
        }
      }

      for (int i = 0; i < r; i++)
        if (ret[i] == NULL)
          ret[i] = null;
      T1[] actualRet = (T1[]) Array.newInstance(original.getClass()
          .getComponentType(), r);
      System.arraycopy(ret, 0, actualRet, 0, r);
      return actualRet;
    }

    /**
     * Marks the current value as a null value. If an actual null were
     * returned, adjust would interpret this as meaning the element should
     * be removed from the array, with subsequent indices being affected. If
     * this method is called from {@link DifferenceListenerE}.add, remove,
     * or set, the element's place will be saved and that element in the
     * returned array will be null.
     */
    public void nullElement() {
      isNullElement = true;
    }

    static void mergeSort(int[] order, int[] distances, int start, int end) {
      if (end - start <= 1)
        return;
      int mid = (start + end + 1) / 2;
      mergeSort(order, distances, start, mid);
      mergeSort(order, distances, mid, end);
      while (start < mid && mid < end) {
        if (distances[start] < distances[mid]) // Reverse order
        {
          int temp = distances[mid];
          int temp2 = order[mid];
          for (int i = mid; i > start; i--) {
            distances[i] = distances[i - 1];
            order[i] = order[i - 1];
          }
          distances[start] = temp;
          order[start] = temp2;
          mid++;
        }
        start++;
      }
    }

    private boolean add(int m, Object[] ret, int r) throws E {
      entriesSet[m] = true;
      T1 item = dl.added(modifier[m], m, r);
      if (isNullElement) {
        item = (T1) NULL;
        isNullElement = false;
      }
      // Adjust the incremental modification indexes
      if (item != null) {
        ret[r] = item;
        for (int i = 0; i < oIdxAdj.length; i++)
          if (oIdxAdj[i] >= r)
            oIdxAdj[i]++;
      } else {
        for (; r < ret.length - 1; r++)
          ret[r] = ret[r + 1];
      }
      return item != null;
    }

    private boolean remove(int o, int m, Object[] ret, int r) throws E {
      T1 item = dl.removed(original[o], o, oIdxAdj[o], r);
      if (isNullElement) {
        item = (T1) NULL;
        isNullElement = false;
      }
      // Adjust the incremental modification indexes
      if (item == null) {
        oIdxAdj[o] = -1;
        for (; o < oIdxAdj.length; o++)
          oIdxAdj[o]--;
      } else
        ret[r] = item;
      return item != null;
    }

    private boolean set(int o, int m, Object[] ret, int r) throws E {
      if (entriesSet[m])
        return ret[r] != null;
      if (oIdxAdj[o] > r && oIdxAdj[o] - r <= 10) {
        /*
         * If a few elements have been moved forward several indices, we
         * want to fire a few move events on those elements rather than
         * many move events for a few index differences.
         */
        int o2;
        for (o2 = o - 1; o2 >= 0; o2--)
          if (oMappings[o2] > m)
            set(o2, oMappings[o2], ret, r + oMappings[o2] - m);
      }
      entriesSet[m] = true;
      T1 item = dl.set(original[o], o, oIdxAdj[o], modifier[m], m, r);
      if (isNullElement) {
        item = (T1) NULL;
        isNullElement = false;
      }
      // Adjust the incremental modification indexes
      if (item != null) {
        ret[r] = item;
        int oAdj = oIdxAdj[o];
        if (r > oAdj) { // Element moved forward--decrement incMods up
                // to the new index
          for (int i = 0; i < oIdxAdj.length; i++)
            if (oIdxAdj[i] >= oAdj && oIdxAdj[i] <= r)
              oIdxAdj[i]--;
        } else if (r < oAdj) { // Element moved backward--increment
                    // incMods up to the original index
          for (int i = 0; i < oIdxAdj.length; i++)
            if (oIdxAdj[i] >= r && oIdxAdj[i] < oAdj)
              oIdxAdj[i]++;
        }
      } else {
        oIdxAdj[o] = -1;
        for (int i = 0; i < oIdxAdj.length; i++)
          if (oIdxAdj[i] > r)
            oIdxAdj[i]--;
        for (; r < ret.length - 1; r++)
          ret[r] = ret[r + 1];
      }
      return item != null;
    }
  }

  /**
   * A listener used for {@link #sort(Object [], SortListener)}
   * 
   * @param <T>
   *            The type of object to sort
   */
  public static interface SortListener<T> extends java.util.Comparator<T> {
    /**
     * Called just <u>after</u> two elements are swapped.
     * 
     * @param o1
     *            The first element being moved
     * @param idx1
     *            The index (in the pre-swap array) of the first element
     * @param o2
     *            The second element being moved
     * @param idx2
     *            The index (in the pre-swap array) of the second element
     */
    void swapped(T o1, int idx1, T o2, int idx2);
  }

  /**
   * Sorts an array, allowing for complex operations during the sort, such as
   * sorting arrays in parallel.
   * 
   * This is an implementation of selection sort. Selection sort is used
   * because although it may perform many more comparison operations than
   * other methods, there is no other method that performs fewer swaps. It is
   * anticipated that this algorithm will be used most often in the case that
   * multiple operations must take place during a swap, while comparison
   * should be fairly quick.
   * 
   * @param <T>
   *            The type of the array to sort
   * @param array
   *            The array to sort
   * @param listener
   *            The listener to use for comparisons and to notify of swapping
   */
  public static <T> void sort(T[] array, SortListener<T> listener) {
    for (int i = 0; i < array.length - 1; i++) {
      int min = findMin(array, i, listener);
      if (min != i) {
        T ta = array[i];
        T tb = array[min];
        array[i] = tb;
        array[min] = ta;
        listener.swapped(ta, i, tb, min);
      }
    }
  }

  private static <T> int findMin(T[] array, int start,
      SortListener<T> listener) {
    int minIdx = start;
    for (int i = start + 1; i < array.length; i++)
      if (listener.compare(array[i], array[minIdx]) < 0)
        minIdx = i;
    return minIdx;
  }

  /**
   * Represents some statistical information calculated on a set of float data
   */
  public static class ArrayStatistics {
    /**
     * The smallest non-NaN, non-infinite datum encountered by this
     * statistics set
     */
    public float min;

    /**
     * The largest non-NaN, non-infinite datum encountered by this
     * statistics set
     */
    public float max;

    /**
     * The number of NaN data encountered by this statistics set
     */
    public int naNCount;

    /**
     * The number of +Inf data encountered by this statistics set
     */
    public int posInfCount;

    /**
     * The number of -Inf data encountered by this statistics set
     */
    public int negInfCount;

    /**
     * The number of non-NaN, non-infinite data encountered by this
     * statistics set
     */
    public int validCount;

    /**
     * The mean value of all non-NaN, non-infinite data encountered by this
     * statistics set
     */
    public float mean;

    /**
     * A running data measure that allows the standard deviation of all
     * non-NaN, non-infinite data encountered by this statistics set
     */
    private float q;

    /**
     * Creates an ArrayStatistics set
     */
    public ArrayStatistics() {
      min = Float.MAX_VALUE;
      max = Float.MIN_VALUE;
      mean = 0;
      q = Float.NaN;
      validCount = 0;
      naNCount = 0;
      posInfCount = 0;
      negInfCount = 0;
    }

    /**
     * Adds a value to this statistics set
     * 
     * @param value
     *            The value to analyze
     */
    public void inputValue(float value) {
      if (Float.isNaN(value))
        naNCount++;
      else if (Float.isInfinite(value)) {
        if (value > 0)
          posInfCount++;
        else
          negInfCount++;
      } else if (validCount == 0) {
        mean = value;
        q = 0;
        validCount++;
      } else {
        if (value < min)
          min = value;
        if (value > max)
          max = value;
        q = q + (validCount * (mean - value) * (mean - value))
            / (validCount + 1);
        validCount++;
        mean = mean + (value - mean) / validCount;
      }
    }

    /**
     * @return The standard deviation of all non-NaN, non-infinite data
     *         encountered by this statistics set
     */
    public float getSigma() {
      return (float) Math.sqrt(q / (validCount - 1));
    }

    /**
     * @return The total number of data encountered by this statistics set
     */
    public int getTotalCount() {
      return validCount + naNCount + posInfCount + negInfCount;
    }

    @Override
    public String toString() {
      StringBuilder ret = new StringBuilder();
      ret.append(getTotalCount());
      ret.append(" values ranged ");
      ret.append(min);
      ret.append(" to ");
      ret.append(max);
      ret.append("; mean=");
      ret.append(mean);
      ret.append(", st.dev.=");
      ret.append(getSigma());
      ret.append("; ");
      ret.append(naNCount);
      ret.append("NaN, ");
      ret.append(posInfCount);
      ret.append("+Inf, ");
      ret.append(negInfCount);
      ret.append("-Inf");
      return ret.toString();
    }
  }

  /**
   * Performs a simple statistical analysis of an array of floats
   * 
   * @param array
   *            An n-dimensional array of floats
   * @param stats
   *            The ArrayStatistics object to populate, or null to create a
   *            new one
   * @return The statistics object
   */
  public static ArrayStatistics getStatistics(Object array,
      ArrayStatistics stats) {
    if (array == null)
      return null;
    if (stats == null)
      stats = new ArrayStatistics();
    if (array instanceof Object[]) {
      for (Object subArray : (Object[]) array)
        getStatistics(subArray, stats);
      return stats;
    }
    if (!(array instanceof float[]))
      throw new IllegalArgumentException(
          "Cannot statistically analyze a "
              + array.getClass().getName());
    for (float f : (float[]) array)
      stats.inputValue(f);
    return stats;
  }

  /**
   * A testing method
   * 
   * @param args
   *            The command-line arguments. The first argument is used to
   *            determine what test to run.
   */
  public static void main(String[] args) {
    String test;
    if (args.length == 0)
      test = "intSort";
    else
      test = args[0];
    if (test.equals("intSort")) {
      int[] random = new int[10000];
      int[] random2 = new int[random.length];
      for (int i = 0; i < random.length; i++)
        random[i] = (int) (Math.random() * Integer.MAX_VALUE);
      ArrayAdjuster.mergeSort(random2, random, 0, random.length);
      boolean sorted = true;
      for (int i = 0; i < random.length - 1; i++)
        if (random[i] < random[i + 1]) {
          sorted = false;
          break;
        }
      System.out.println("Sort " + (sorted ? "succeeded" : "failed"));
    } else if (test.equals("adjust")) {
      final Integer[] start = new Integer[25];
      for (int i = 0; i < start.length; i++)
        start[i] = new Integer(i);
      Integer[] modifier = new Integer[start.length];
      for (int i = 0; i < modifier.length; i++) {
        if (i % 5 != 0)
          modifier[i] = new Integer(i);
        else
          modifier[i] = new Integer(i / 5 * start.length);
      }
      Integer[] result = adjust(start, modifier,
          new DifferenceListener<Integer, Integer>() {
            public boolean identity(Integer o1, Integer o2) {
              return o1.equals(o2);
            }

            public Integer added(Integer o, int mIdx, int retIdx) {
              return o;
            }

            public Integer removed(Integer o, int oIdx,
                int oIdxAdj, int retIdx) {
              return o;
            }

            public Integer set(Integer o1, int idx1, int oIdxAdj,
                Integer o2, int idx2, int retIdx) {
              if (o1.intValue() % 5 == 2)
                return null;
              return o1;
            }
          });
      System.out.println("Original array=" + toString(start));
      System.out.println("Modifier array=" + toString(modifier));
      System.out.println("Adjusted array=" + toString(result));
    } else
      throw new IllegalArgumentException("Unrecognized test: " + test);
  }
}

   
    
    
    
    
    
  








Related examples in the same category

1.Creating and storing arrays in a map
2.Sort based on the values
3.Get a key from value with an HashMap
4.Retrieve environment variables (JDK1.5)
5.Creating a Type-Specific Map: creates a map whose keys are Integer objects and values are String objects.
6.A map declared to hold objects of a type T can also hold objects that extend from T
7.A value retrieved from a type-specific collection does not need to be casted
8.Map techniques.
9.Create an array containing the keys in a map
10.Create an array containing the values in a map
11.Creating a Hash Table
12.Creating a Map That Retains Order-of-Insertion
13.Automatically Removing an Unreferenced Element from a Hash Table
14.Creating a Type-Specific Map [5.0]
15.Use Iterator to loop through the HashMap class
16.Create type specific collections
17.Convert Properties into Map
18.A java.util.Map implementation using reference values
19.Utility method that return a String representation of a map. The elements will be represented as "key = value"
20.Utility method that return a String representation of a map. The elements will be represented as "key = value" (tab)
21.This program demonstrates the use of a map with key type String and value type Employee
22.Format a Map
23.A Map implementation that dumps its content when memory runs low.
24.A Map that stores the values in files within a directory.
25.Map List
26.Multi Value Map Array List
27.Multi Value Map Linked HashSet
28.An object that maps keys to values, and values back to keys.
29.LRU Map
30.A map acts like array.
31.Order Retaining Map
32.BinaryMap class implements a map from objects to integer objects where the only value is the integer with value 1.
33.A space-optimized map for associating char keys with values.
34.A Map implementation that grows to a fixed size and then retains only a fixed number of the highest (largest) keys.
35.Class which creates mapping between keys and a list of values.
36.History Map
37.Sorts map by values in ascending order.
38.Map from a given key to a list of values
39.Map from a given key to a set of values
40.Class which keeps a set of values and assigns each value a unique positive index.
41.Array Map
42.Array map
43.An ArrayMap is a very inefficient map type that is more robust in dealing with changes to its keys than other maps.
44.This Map stores it's keys as strings in upper case, null and duplicate keys are not allowed
45.Map to string
46.A simple class that stores key Strings as char[]'s in a hash table.