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

com.cedarsoftware.util.ArrayUtilities Maven / Gradle / Ivy

The newest version!
package com.cedarsoftware.util;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;

/**
 * A utility class that provides various static methods for working with Java arrays.
 * 

* {@code ArrayUtilities} simplifies common array operations, such as checking for emptiness, * combining arrays, creating subsets, and converting collections to arrays. It includes * methods that are null-safe and type-generic, making it a flexible and robust tool * for array manipulation in Java. *

* *

Key Features

*
    *
  • Immutable common arrays for common use cases, such as {@link #EMPTY_OBJECT_ARRAY} and {@link #EMPTY_BYTE_ARRAY}.
  • *
  • Null-safe utility methods for checking array emptiness, size, and performing operations like shallow copying.
  • *
  • Support for generic array creation and manipulation, including: *
      *
    • Combining multiple arrays into a new array ({@link #addAll}).
    • *
    • Removing an item from an array by index ({@link #removeItem}).
    • *
    • Creating subsets of an array ({@link #getArraySubset}).
    • *
    *
  • *
  • Conversion utilities for working with arrays and collections, such as converting a {@link Collection} to an array * of a specified type ({@link #toArray}).
  • *
* *

Usage Examples

*
{@code
 * // Check if an array is empty
 * boolean isEmpty = ArrayUtilities.isEmpty(new String[] {});
 *
 * // Combine two arrays
 * String[] combined = ArrayUtilities.addAll(new String[] {"a", "b"}, new String[] {"c", "d"});
 *
 * // Create a subset of an array
 * int[] subset = ArrayUtilities.getArraySubset(new int[] {1, 2, 3, 4, 5}, 1, 4); // {2, 3, 4}
 *
 * // Convert a collection to a typed array
 * List list = List.of("x", "y", "z");
 * String[] array = ArrayUtilities.toArray(String.class, list);
 * }
* *

Performance Notes

*
    *
  • Methods like {@link #isEmpty} and {@link #size} are optimized for performance but remain null-safe.
  • *
  • Some methods, such as {@link #toArray} and {@link #addAll}, involve array copying and may incur performance * costs for very large arrays.
  • *
* *

Design Philosophy

*

* This utility class is designed to simplify array operations in a type-safe and null-safe manner. * It avoids duplicating functionality already present in the JDK while extending support for * generic and collection-based workflows. *

* * @author Ken Partlow ([email protected]) * @author John DeRegnaucourt ([email protected]) *
* Copyright (c) Cedar Software LLC *

* 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 *

* License *

* 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. */ public final class ArrayUtilities { /** * Immutable common arrays. */ public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; public static final char[] EMPTY_CHAR_ARRAY = new char[0]; public static final Character[] EMPTY_CHARACTER_ARRAY = new Character[0]; public static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; /** * Private constructor to promote using as static class. */ private ArrayUtilities() { super(); } /** * This is a null-safe isEmpty check. It uses the Array * static class for doing a length check. This check is actually * .0001 ms slower than the following typed check: *

* return array == null || array.length == 0; *

* but gives you more flexibility, since it checks for all array * types. * * @param array array to check * @return true if empty or null */ public static boolean isEmpty(final Object array) { return array == null || Array.getLength(array) == 0; } /** * Null-safe check whether the given array contains at least one element. * * @param array array to check * @return {@code true} if array is non-null and has a positive length */ public static boolean isNotEmpty(final Object array) { return !isEmpty(array); } /** * Returns the size (length) of the specified array in a null-safe manner. *

* If the provided array is {@code null}, this method returns {@code 0}. * Otherwise, it returns the length of the array using {@link Array#getLength(Object)}. *

* *

Usage Example

*
{@code
     * int[] numbers = {1, 2, 3};
     * int size = ArrayUtilities.size(numbers); // size == 3
     *
     * int sizeOfNull = ArrayUtilities.size(null); // sizeOfNull == 0
     * }
* * @param array the array whose size is to be determined, may be {@code null} * @return the size of the array, or {@code 0} if the array is {@code null} */ public static int size(final Object array) { return array == null ? 0 : Array.getLength(array); } /** *

Shallow copies an array of Objects *

*

The objects in the array are not cloned, thus there is no special * handling for multidimensional arrays. *

*

This method returns null if null array input.

* * @param array the array to shallow clone, may be null * @param the array type * @return the cloned array, null if null input */ public static T[] shallowCopy(final T[] array) { if (array == null) { return null; } return array.clone(); } /** * Return the supplied array, or an empty array if {@code null}. * * @param componentType the component type for the empty array when {@code array} is {@code null} * @param array array which may be {@code null} * @param array component type * @return the original array, or a new empty array of the specified type if {@code array} is {@code null} */ @SuppressWarnings("unchecked") public static T[] nullToEmpty(Class componentType, T[] array) { Objects.requireNonNull(componentType, "componentType is null"); return array == null ? (T[]) Array.newInstance(componentType, 0) : array; } /** * Creates and returns an array containing the provided elements. * *

This method accepts a variable number of arguments and returns them as an array of type {@code T[]}. * It is primarily used to facilitate array creation in generic contexts, where type inference is necessary. * *

Example Usage: *

{@code
     * String[] stringArray = createArray("Apple", "Banana", "Cherry");
     * Integer[] integerArray = createArray(1, 2, 3, 4);
     * Person[] personArray = createArray(new Person("Alice"), new Person("Bob"));
     * }
* *

Important Considerations: *

    *
  • Type Safety: Due to type erasure in Java generics, this method does not perform any type checks * beyond what is already enforced by the compiler. Ensure that all elements are of the expected type {@code T} to avoid * {@code ClassCastException} at runtime.
  • *
  • Heap Pollution: The method is annotated with {@link SafeVarargs} to suppress warnings related to heap * pollution when using generics with varargs. It is safe to use because the method does not perform any unsafe operations * on the varargs parameter.
  • *
  • Null Elements: The method does not explicitly handle {@code null} elements. If {@code null} values * are passed, they will be included in the returned array.
  • *
* * @param the component type of the array * @param elements the elements to be stored in the array * @return an array containing the provided elements * @throws NullPointerException if the {@code elements} array is {@code null} */ @SafeVarargs public static T[] createArray(T... elements) { if (elements == null) { return null; } return Arrays.copyOf(elements, elements.length); } /** *

Adds all the elements of the given arrays into a new array. *

*

The new array contains all the element of array1 followed * by all the elements array2. When an array is returned, it is always * a new array. *

*
     * ArrayUtilities.addAll(null, null)     = null
     * ArrayUtilities.addAll(array1, null)   = cloned copy of array1
     * ArrayUtilities.addAll(null, array2)   = cloned copy of array2
     * ArrayUtilities.addAll([], [])         = []
     * ArrayUtilities.addAll([null], [null]) = [null, null]
     * ArrayUtilities.addAll(["a", "b", "c"], ["1", "2", "3"]) = ["a", "b", "c", "1", "2", "3"]
     * 
* * @param array1 the first array whose elements are added to the new array, may be null * @param array2 the second array whose elements are added to the new array, may be null * @param the array type * @return The new array, null if null array inputs. * The type of the new array is the type of the first array. */ @SuppressWarnings("unchecked") public static T[] addAll(final T[] array1, final T[] array2) { if (array1 == null) { return shallowCopy(array2); } else if (array2 == null) { return shallowCopy(array1); } final T[] newArray = (T[]) Array.newInstance(array1.getClass().getComponentType(), array1.length + array2.length); System.arraycopy(array1, 0, newArray, 0, array1.length); System.arraycopy(array2, 0, newArray, array1.length, array2.length); return newArray; } /** * Removes an element at the specified position from an array, returning a new array with the element removed. *

* This method creates a new array with length one less than the input array and copies all elements * except the one at the specified position. The original array remains unchanged. *

* *

Example:

*
{@code
     * Integer[] numbers = {1, 2, 3, 4, 5};
     * Integer[] result = ArrayUtilities.removeItem(numbers, 2);
     * // result = {1, 2, 4, 5}
     * }
* * @param array the source array from which to remove an element * @param pos the position of the element to remove (zero-based) * @param the component type of the array * @return a new array containing all elements from the original array except the element at the specified position * @throws ArrayIndexOutOfBoundsException if {@code pos} is negative or greater than or equal to the array length * @throws NullPointerException if the input array is null */ @SuppressWarnings("unchecked") public static T[] removeItem(T[] array, int pos) { Objects.requireNonNull(array, "array cannot be null"); final int len = array.length; if (pos < 0 || pos >= len) { throw new ArrayIndexOutOfBoundsException("Index: " + pos + ", Length: " + len); } T[] dest = (T[]) Array.newInstance(array.getClass().getComponentType(), len - 1); System.arraycopy(array, 0, dest, 0, pos); System.arraycopy(array, pos + 1, dest, pos, len - pos - 1); return dest; } /** * Append a single element to an array, returning a new array containing the element. * * @param componentType component type for the array when {@code array} is {@code null} * @param array existing array, may be {@code null} * @param item element to append * @param array component type * @return new array with {@code item} appended */ @SuppressWarnings("unchecked") public static T[] addItem(Class componentType, T[] array, T item) { Objects.requireNonNull(componentType, "componentType is null"); if (array == null) { T[] result = (T[]) Array.newInstance(componentType, 1); result[0] = item; return result; } T[] newArray = Arrays.copyOf(array, array.length + 1); newArray[array.length] = item; return newArray; } /** * Locate the first index of {@code item} within {@code array}. * * @param array array to search * @param item item to locate * @param array component type * @return index of the item or {@code -1} if not found or array is {@code null} */ public static int indexOf(T[] array, T item) { if (array == null) { return -1; } for (int i = 0; i < array.length; i++) { if (Objects.equals(array[i], item)) { return i; } } return -1; } /** * Locate the last index of {@code item} within {@code array}. * * @param array array to search * @param item item to locate * @param array component type * @return index of the item or {@code -1} if not found or array is {@code null} */ public static int lastIndexOf(T[] array, T item) { if (array == null) { return -1; } for (int i = array.length - 1; i >= 0; i--) { if (Objects.equals(array[i], item)) { return i; } } return -1; } /** * Determine whether the provided array contains the specified item. * * @param array the array to search, may be {@code null} * @param item the item to find * @param the array component type * @return {@code true} if the item exists in the array; {@code false} otherwise */ public static boolean contains(T[] array, T item) { return indexOf(array, item) >= 0; } /** * Creates a new array containing elements from the specified range of the source array. *

* Returns a new array containing elements from index {@code start} (inclusive) to index {@code end} (exclusive). * The original array remains unchanged. *

* *

Example:

*
{@code
     * String[] words = {"apple", "banana", "cherry", "date", "elderberry"};
     * String[] subset = ArrayUtilities.getArraySubset(words, 1, 4);
     * // subset = {"banana", "cherry", "date"}
     * }
* * @param array the source array from which to extract elements * @param start the initial index of the range, inclusive * @param end the final index of the range, exclusive * @param the component type of the array * @return a new array containing the specified range from the original array * @throws ArrayIndexOutOfBoundsException if {@code start} is negative, {@code end} is greater than the array length, * or {@code start} is greater than {@code end} * @throws NullPointerException if the input array is null * @see Arrays#copyOfRange(Object[], int, int) */ public static T[] getArraySubset(T[] array, int start, int end) { return Arrays.copyOfRange(array, start, end); } /** * Convert Collection to a Java (typed) array []. * * @param classToCastTo array type (Object[], Person[], etc.) * @param c Collection containing items to be placed into the array. * @param Type of the array * @return Array of the type (T) containing the items from collection 'c'. */ @SuppressWarnings("unchecked") public static T[] toArray(Class classToCastTo, Collection c) { Objects.requireNonNull(classToCastTo, "classToCastTo is null"); Objects.requireNonNull(c, "collection is null"); T[] array = (T[]) Array.newInstance(classToCastTo, c.size()); return c.toArray(array); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy