All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.rapiddweller.common.ArrayUtil Maven / Gradle / Ivy

Go to download

'rapiddweller Common' is an open source Java library forked from Databene Commons by Volker Bergmann. It provides extensions to the Java core library by utility classes, abstract concepts and concrete implementations.

There is a newer version: 2.0.1-jdk-11
Show newest version
/*
 * Copyright (C) 2004-2015 Volker Bergmann ([email protected]).
 * All rights reserved.
 *
 * 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.
 */

package com.rapiddweller.common;

import com.rapiddweller.common.iterator.ArrayIterator;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

/**
 * Provides array-related operations.
 * Created: 09.06.2006 21:31:49
 *
 * @author Volker Bergmann
 * @since 0.1
 */
public final class ArrayUtil {

  /**
   * Binary search int.
   *
   * @param               the type parameter
   * @param array            the array
   * @param item             the item
   * @param comparator       the comparator
   * @param resultIfNotFound the result if not found
   * @return the int
   */
  public static  int binarySearch(T[] array, T item, Comparator comparator, UnfoundResult resultIfNotFound) {
    int index = Arrays.binarySearch(array, item, comparator);
    if (index >= 0) {
      return index;
    } else {
      switch (resultIfNotFound) {
        case PREV:
          return -index - 2;
        case NEXT:
          return -index - 1;
        case NEG:
          return -1;
        default:
          throw new UnsupportedOperationException("Not a supported option: " + resultIfNotFound);
      }
    }
  }

  /**
   * Copy of range t [ ].
   *
   * @param     the type parameter
   * @param array  the array
   * @param offset the offset
   * @param length the length
   * @return the t [ ]
   */
  public static  T[] copyOfRange(T[] array, int offset, int length) {
    return copyOfRange(array, offset, length, componentType(array));
  }

  /**
   * Component type class.
   *
   * @param    the type parameter
   * @param array the array
   * @return the class
   */
  @SuppressWarnings("unchecked")
  public static  Class componentType(T[] array) {
    Class resultType = (Class) array.getClass();
    return (Class) resultType.getComponentType();
  }

  /**
   * Copy of range t [ ].
   *
   * @param            the type parameter
   * @param array         the array
   * @param offset        the offset
   * @param length        the length
   * @param componentType the component type
   * @return the t [ ]
   */
  @SuppressWarnings("unchecked")
  public static  T[] copyOfRange(Object[] array, int offset, int length, Class componentType) {
    T[] result = (T[]) Array.newInstance(componentType, length);
    System.arraycopy(array, offset, result, 0, length);
    return result;
  }

  /**
   * Sub array string [ ].
   *
   * @param array  the array
   * @param offset the offset
   * @return the string [ ]
   */
  public static String[] subArray(String[] array, int offset) {
    String[] result = new String[array.length - offset];
    System.arraycopy(array, offset, result, 0, array.length - offset);
    return result;
  }

  /**
   * Remove element t [ ].
   *
   * @param    the type parameter
   * @param item  the item
   * @param array the array
   * @return the t [ ]
   */
  public static  T[] removeElement(T item, T[] array) {
    int index = indexOf(item, array);
    return remove(index, array);
  }

  /**
   * Remove t [ ].
   *
   * @param            the type parameter
   * @param indexToRemove the index to remove
   * @param array         the array
   * @return the t [ ]
   */
  @SuppressWarnings("unchecked")
  public static  T[] remove(int indexToRemove, T[] array) {
    Class componentType = componentType(array);
    T[] result = (T[]) Array.newInstance(componentType, array.length - 1);
    if (indexToRemove > 0) {
      System.arraycopy(array, 0, result, 0, indexToRemove);
    }
    System.arraycopy(array, indexToRemove + 1, result, indexToRemove, array.length - indexToRemove - 1);
    return result;
  }

  /**
   * Remove all t [ ].
   *
   * @param       the type parameter
   * @param toRemove the to remove
   * @param target   the target
   * @return the t [ ]
   */
  public static  T[] removeAll(T[] toRemove, T[] target) {
    Class componentType = componentType(target);
    ArrayBuilder builder = new ArrayBuilder<>(componentType);
    for (T element : target) {
      if (!contains(element, toRemove)) {
        builder.add(element);
      }
    }
    return builder.toArray();
  }

  // containment check -----------------------------------------------------------------------------------------------

  /**
   * Tells if an array contains a specific element
   *
   * @param element the element to search
   * @param array   the array to scan
   * @return true if the element was found, else false
   */
  public static boolean contains(Object element, Object array) {
    int length = Array.getLength(array);
    for (int i = 0; i < length; i++) {
      Object o = Array.get(array, i);
      if (NullSafeComparator.equals(o, element)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Contains all boolean.
   *
   * @param         the type parameter
   * @param subArray   the sub array
   * @param superArray the super array
   * @return the boolean
   */
  public static  boolean containsAll(T[] subArray, T[] superArray) {
    for (T t : subArray) {
      if (!contains(t, superArray)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Index of int.
   *
   * @param subArray the sub array
   * @param array    the array
   * @return the int
   */
  public static int indexOf(byte[] subArray, byte[] array) {
    return indexOf(subArray, 0, array);
  }

  /**
   * Index of int.
   *
   * @param subArray  the sub array
   * @param fromIndex the from index
   * @param array     the array
   * @return the int
   */
  public static int indexOf(byte[] subArray, int fromIndex, byte[] array) {
    for (int i = fromIndex; i <= array.length - subArray.length; i++) {
      boolean match = true;
      for (int j = 0; j < subArray.length; j++) {
        if (array[i + j] != subArray[j]) {
          match = false;
        }
      }
      if (match) {
        return i;
      }
    }
    return -1;
  }

  /**
   * Ends with sequence boolean.
   *
   * @param         the type parameter
   * @param candidates the candidates
   * @param searched   the searched
   * @return the boolean
   */
  public static  boolean endsWithSequence(T[] candidates, T[] searched) {
    if (searched.length > candidates.length) {
      return false;
    }
    for (int i = 0; i < searched.length; i++) {
      if (!candidates[candidates.length - searched.length + i].equals(searched[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Common elements t [ ].
   *
   * @param      the type parameter
   * @param sources the sources
   * @return the t [ ]
   */
  @SuppressWarnings("unchecked")
  public static  T[] commonElements(T[]... sources) {
    Class componentType = null;
    for (int arrayNumber = 0; arrayNumber < sources.length && componentType == null; arrayNumber++) {
      T[] source = sources[arrayNumber];
      for (int index = 0; index < source.length && componentType == null; index++) {
        if (source[index] != null) {
          componentType = (Class) source[index].getClass();
        }
      }
    }
    return commonElements(componentType, sources);
  }

  /**
   * Common elements t [ ].
   *
   * @param            the type parameter
   * @param componentType the component type
   * @param sources       the sources
   * @return the t [ ]
   */
  @SafeVarargs
  public static  T[] commonElements(Class componentType, T[]... sources) {
    ArrayBuilder builder = new ArrayBuilder<>(componentType);
    T[] firstArray = sources[0];
    for (T element : firstArray) {
      boolean common = true;
      for (int i = 1; i < sources.length; i++) {
        if (!ArrayUtil.contains(element, sources[i])) {
          common = false;
          break;
        }
      }
      if (common) {
        builder.add(element);
      }
    }
    return builder.toArray();
  }


  // identity checks -------------------------------------------------------------------------------------------------

  /**
   * Equals ignore order boolean.
   *
   * @param  the type parameter
   * @param a1  the a 1
   * @param a2  the a 2
   * @return the boolean
   */
  public static  boolean equalsIgnoreOrder(T[] a1, T[] a2) {
    if (a1 == a2) {
      return true;
    }
    if (a1 == null) {
      return false;
    }
    if (a1.length != a2.length) {
      return false;
    }
    List l1 = new ArrayList<>(a1.length);
    Collections.addAll(l1, a1);
    for (int i = a1.length - 1; i >= 0; i--) {
      if (contains(a1[i], a2)) {
        l1.remove(i);
      } else {
        return false;
      }
    }
    return l1.size() == 0;
  }

  /**
   * Equals boolean.
   *
   * @param a1 the a 1
   * @param a2 the a 2
   * @return the boolean
   */
  public static boolean equals(Object a1, Object a2) {
    if (a1 == a2) {
      return true;
    }
    if (a1 == null || !(a1.getClass().isArray()) || !(a2.getClass().isArray())) {
      return false;
    }
    int length = Array.getLength(a1);
    if (length != Array.getLength(a2)) {
      return false;
    }
    List l1 = new ArrayList<>(length);
    for (int i = 0; i < length; i++) {
      l1.add(Array.get(a1, i));
    }
    for (int i = length - 1; i >= 0; i--) {
      if (contains(Array.get(a1, i), a2)) {
        l1.remove(i);
      } else {
        return false;
      }
    }
    return l1.size() == 0;
  }

  /**
   * Index of int.
   *
   * @param           the type parameter
   * @param searchedItem the searched item
   * @param array        the array
   * @return the int
   */
  public static  int indexOf(T searchedItem, T[] array) {
    for (int i = 0; i < array.length; i++) {
      T candidate = array[i];
      if (NullSafeComparator.equals(candidate, searchedItem)) {
        return i;
      }
    }
    return -1;
  }

  /**
   * To array t [ ].
   *
   * @param     the type parameter
   * @param values the values
   * @return the t [ ]
   */
  @SuppressWarnings("unchecked")
  public static  T[] toArray(T... values) {
    Class componentType = (Class) (values.length > 0 ? values[0].getClass() : Object.class);
    return buildObjectArrayOfType(componentType, values);
  }

  /**
   * To int array int [ ].
   *
   * @param values the values
   * @return the int [ ]
   */
  public static int[] toIntArray(int... values) {
    int[] array = new int[values.length];
    System.arraycopy(values, 0, array, 0, values.length);
    return array;
  }

  /**
   * To char array char [ ].
   *
   * @param values the values
   * @return the char [ ]
   */
  public static char[] toCharArray(char... values) {
    char[] array = new char[values.length];
    System.arraycopy(values, 0, array, 0, values.length);
    return array;
  }

  /**
   * Build array of type object.
   *
   * @param componentType the component type
   * @param values        the values
   * @return the object
   */
  public static Object buildArrayOfType(Class componentType, Object... values) {
    Object array = Array.newInstance(componentType, values.length);
    for (int i = 0; i < values.length; i++) {
      Array.set(array, i, values[i]); // explicit assignment since System.arraycopy() does not perform autoboxing
    }
    return array;
  }

  /**
   * Build object array of type t [ ].
   *
   * @param            the type parameter
   * @param componentType the component type
   * @param values        the values
   * @return the t [ ]
   */
  @SuppressWarnings("unchecked")
  public static  T[] buildObjectArrayOfType(Class componentType, T... values) {
    T[] array = (T[]) Array.newInstance(componentType, values.length);
    System.arraycopy(values, 0, array, 0, values.length);
    return array;
  }

  /**
   * Iterator iterator.
   *
   * @param    the type parameter
   * @param array the array
   * @return the iterator
   */
  public static  Iterator iterator(T[] array) {
    return new ArrayIterator<>(array);
  }

  /**
   * Revert t [ ].
   *
   * @param    the type parameter
   * @param array the array
   * @return the t [ ]
   */
  public static  T[] revert(T[] array) {
    for (int i = (array.length >> 1) - 1; i >= 0; i--) {
      T tmp = array[i];
      array[i] = array[array.length - 1 - i];
      array[array.length - 1 - i] = tmp;
    }
    return array;
  }

  /**
   * Revert char [ ].
   *
   * @param array the array
   * @return the char [ ]
   */
  public static char[] revert(char[] array) {
    for (int i = (array.length >> 1) - 1; i >= 0; i--) {
      char tmp = array[i];
      array[i] = array[array.length - 1 - i];
      array[array.length - 1 - i] = tmp;
    }
    return array;
  }

  /**
   * Array type class.
   *
   * @param componentType the component type
   * @return the class
   */
  @SuppressWarnings("rawtypes")
  public static Class arrayType(Class componentType) { // this cannot be made generic since it needs to support simple types too
    if (componentType == byte.class) {
      return byte[].class;
    } else if (componentType == char.class) {
      return char[].class;
    } else if (componentType == int.class) {
      return int[].class;
    } else if (componentType == long.class) {
      return long[].class;
    } else if (componentType == short.class) {
      return short[].class;
    } else if (componentType == double.class) {
      return double[].class;
    } else if (componentType == float.class) {
      return float[].class;
    } else if (componentType == boolean.class) {
      return boolean[].class;
    }
    Object[] array = (Object[]) Array.newInstance(componentType, 0);
    return array.getClass();
  }

  /**
   * New instance t [ ].
   *
   * @param            the type parameter
   * @param componentType the component type
   * @param length        the length
   * @return the t [ ]
   */
  @SuppressWarnings("unchecked")
  public static  T[] newInstance(Class componentType, int length) {
    return (T[]) Array.newInstance(componentType, length);
  }

  /**
   * Merge t [ ].
   *
   * @param     the type parameter
   * @param first  the first
   * @param second the second
   * @return the t [ ]
   */
  public static  T[] merge(T[] first, T[] second) {
    return append(second, first);
  }

  /**
   * Append t [ ].
   *
   * @param        the type parameter
   * @param newValues the new values
   * @param array     the array
   * @return the t [ ]
   */
  public static  T[] append(T[] newValues, T[] array) {
    if (array == null) {
      return (newValues != null ? newValues.clone() : null);
    } else if (newValues == null) {
      return array.clone();
    } else {
      T[] newArray = newInstance(componentType(array), array.length + newValues.length);
      System.arraycopy(array, 0, newArray, 0, array.length);
      for (int i = 0; i < newValues.length; i++) {
        newArray[array.length + i] = newValues[i];
      }
      return newArray;
    }
  }

  /**
   * Append t [ ].
   *
   * @param    the type parameter
   * @param value the value
   * @param array the array
   * @return the t [ ]
   */
  @SuppressWarnings("unchecked")
  public static  T[] append(T value, T[] array) {
    if (array == null) {
      return toArray(value);
    } else {
      T[] newArray = newInstance(componentType(array), array.length + 1);
      System.arraycopy(array, 0, newArray, 0, array.length);
      newArray[array.length] = value;
      return newArray;
    }
  }

  /**
   * Append byte [ ].
   *
   * @param value the value
   * @param array the array
   * @return the byte [ ]
   */
  public static byte[] append(byte value, byte[] array) {
    if (array == null) {
      return new byte[] {value};
    } else {
      byte[] newArray = new byte[array.length + 1];
      System.arraycopy(array, 0, newArray, 0, array.length);
      newArray[array.length] = value;
      return newArray;
    }
  }

  /**
   * Is empty boolean.
   *
   * @param values the values
   * @return the boolean
   */
  public static boolean isEmpty(Object values) {
    return (values == null || Array.getLength(values) == 0);
  }

  /**
   * Last element of t.
   *
   * @param    the type parameter
   * @param array the array
   * @return the t
   */
  public static  T lastElementOf(T[] array) {
    if (isEmpty(array)) {
      return null;
    }
    return array[array.length - 1];
  }

  /**
   * Last element of integer.
   *
   * @param array the array
   * @return the integer
   */
  public static Integer lastElementOf(int[] array) {
    if (array == null || array.length == 0) {
      return -1;
    }
    return array[array.length - 1];
  }

  /**
   * All null boolean.
   *
   * @param values the values
   * @return the boolean
   */
  public static boolean allNull(Object[] values) {
    if (values == null) {
      return true;
    }
    for (Object value : values) {
      if (value != null) {
        return false;
      }
    }
    return true;
  }

  /**
   * The enum Unfound result.
   */
  public enum UnfoundResult {
    /**
     * Prev unfound result.
     */
    PREV,
    /**
     * Next unfound result.
     */
    NEXT,
    /**
     * Neg unfound result.
     */
    NEG
  }

}