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

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

The newest version!
package com.cedarsoftware.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.NavigableSet;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;

import com.cedarsoftware.util.convert.CollectionsWrappers;

/**
 * A utility class providing enhanced operations for working with Java collections.
 * 

* {@code CollectionUtilities} simplifies tasks such as null-safe checks, retrieving collection sizes, * creating immutable collections, and wrapping collections in checked, synchronized, or unmodifiable views. * It includes functionality compatible with JDK 8, providing alternatives to methods introduced in later * versions of Java, such as {@link java.util.List#of(Object...)} and {@link java.util.Set#of(Object...)}. *

* *

Key Features

*
    *
  • Null-Safe Checks: *
      *
    • {@link #isEmpty(Collection)}: Checks if a collection is null or empty.
    • *
    • {@link #hasContent(Collection)}: Checks if a collection is not null and contains at least one element.
    • *
    • {@link #size(Collection)}: Safely retrieves the size of a collection, returning {@code 0} if it is null.
    • *
    *
  • *
  • Immutable Collection Creation: *
      *
    • {@link #listOf(Object...)}: Creates an immutable list of specified elements, compatible with JDK 8.
    • *
    • {@link #setOf(Object...)}: Creates an immutable set of specified elements, compatible with JDK 8.
    • *
    *
  • *
  • Collection Wrappers: *
      *
    • {@link #getUnmodifiableCollection(Collection)}: Wraps a collection in the most specific * unmodifiable view based on its type (e.g., {@link NavigableSet}, {@link SortedSet}, {@link List}).
    • *
    • {@link #getCheckedCollection(Collection, Class)}: Wraps a collection in the most specific * type-safe checked view based on its type (e.g., {@link NavigableSet}, {@link SortedSet}, {@link List}).
    • *
    • {@link #getSynchronizedCollection(Collection)}: Wraps a collection in the most specific * thread-safe synchronized view based on its type (e.g., {@link NavigableSet}, {@link SortedSet}, {@link List}).
    • *
    • {@link #getEmptyCollection(Collection)}: Returns an empty collection of the same type as the input * collection (e.g., {@link NavigableSet}, {@link SortedSet}, {@link List}).
    • *
    *
  • *
* *

Usage Examples

*
{@code
 * // Null-safe checks
 * boolean isEmpty = CollectionUtilities.isEmpty(myCollection);
 * boolean hasContent = CollectionUtilities.hasContent(myCollection);
 * int size = CollectionUtilities.size(myCollection);
 *
 * // Immutable collections
 * List list = CollectionUtilities.listOf("A", "B", "C");
 * Set set = CollectionUtilities.setOf("X", "Y", "Z");
 *
 * // Collection wrappers
 * Collection unmodifiable = CollectionUtilities.getUnmodifiableCollection(myCollection);
 * Collection checked = CollectionUtilities.getCheckedCollection(myCollection, String.class);
 * Collection synchronizedCollection = CollectionUtilities.getSynchronizedCollection(myCollection);
 * Collection empty = CollectionUtilities.getEmptyCollection(myCollection);
 * }
* *

Design Notes

*
    *
  • This class is designed as a static utility class and should not be instantiated.
  • *
  • It uses unmodifiable empty collections as constants to optimize memory usage and prevent unnecessary object creation.
  • *
  • The collection wrappers apply type-specific operations based on the runtime type of the provided collection.
  • *
* * @see java.util.Collection * @see java.util.List * @see java.util.Set * @see Collections * @see Collections#unmodifiableCollection(Collection) * @see Collections#checkedCollection(Collection, Class) * @see Collections#synchronizedCollection(Collection) * @see Collections#emptyList() * @see Collections#emptySet() * * @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 class CollectionUtilities { private static final Set unmodifiableEmptySet = Collections.unmodifiableSet(new HashSet<>()); private static final List unmodifiableEmptyList = Collections.unmodifiableList(new ArrayList<>()); private static final Class unmodifiableCollectionClass = CollectionsWrappers.getUnmodifiableCollectionClass(); private static final Class synchronizedCollectionClass = CollectionsWrappers.getSynchronizedCollectionClass(); private CollectionUtilities() { } /** * This is a null-safe isEmpty check. * * @param col the collection to check, may be {@code null} * @return {@code true} if the collection is {@code null} or empty; {@code false} otherwise */ public static boolean isEmpty(Collection col) { return col == null || col.isEmpty(); } /** * Checks if the specified collection is not {@code null} and contains at least one element. *

* This method provides a null-safe way to verify that a collection has content, returning {@code false} * if the collection is {@code null} or empty. *

* * @param col the collection to check, may be {@code null} * @return {@code true} if the collection is not {@code null} and contains at least one element; * {@code false} otherwise */ public static boolean hasContent(Collection col) { return col != null && !col.isEmpty(); } /** * Returns the size of the specified collection in a null-safe manner. *

* If the collection is {@code null}, this method returns {@code 0}. Otherwise, it returns the * number of elements in the collection. *

* * @param col the collection to check, may be {@code null} * @return the size of the collection, or {@code 0} if the collection is {@code null} */ public static int size(Collection col) { return col == null ? 0 : col.size(); } /** * Creates an unmodifiable list containing the specified elements. *

* This method provides functionality similar to {@link java.util.List#of(Object...)} introduced in JDK 9, * but is compatible with JDK 8. If the input array is {@code null} or empty, this method returns * an unmodifiable empty list. *

* *

Usage Example

*
{@code
     * List list = listOf("A", "B", "C"); // Returns an unmodifiable list containing "A", "B", "C"
     * List emptyList = listOf();         // Returns an unmodifiable empty list
     * }
* * @param the type of elements in the list * @param items the elements to be included in the list; may be {@code null} * @return an unmodifiable list containing the specified elements, or an unmodifiable empty list if the input is {@code null} or empty * @throws NullPointerException if any of the elements in the input array are {@code null} * @see Collections#unmodifiableList(List) */ @SafeVarargs @SuppressWarnings("unchecked") public static List listOf(T... items) { if (items == null || items.length == 0) { return (List) unmodifiableEmptyList; } List list = new ArrayList<>(); Collections.addAll(list, items); return Collections.unmodifiableList(list); } /** * Creates an unmodifiable set containing the specified elements. *

* This method provides functionality similar to {@link java.util.Set#of(Object...)} introduced in JDK 9, * but is compatible with JDK 8. If the input array is {@code null} or empty, this method returns * an unmodifiable empty set. *

* *

Usage Example

*
{@code
     * Set set = setOf("A", "B", "C"); // Returns an unmodifiable set containing "A", "B", "C"
     * Set emptySet = setOf();         // Returns an unmodifiable empty set
     * }
* * @param the type of elements in the set * @param items the elements to be included in the set; may be {@code null} * @return an unmodifiable set containing the specified elements, or an unmodifiable empty set if the input is {@code null} or empty * @throws NullPointerException if any of the elements in the input array are {@code null} * @see Collections#unmodifiableSet(Set) */ @SafeVarargs @SuppressWarnings("unchecked") public static Set setOf(T... items) { if (items == null || items.length == 0) { return (Set) unmodifiableEmptySet; } Set set = new LinkedHashSet<>(); Collections.addAll(set, items); return Collections.unmodifiableSet(set); } /** * Determines whether the specified class represents an unmodifiable collection type. *

* This method checks if the provided {@code targetType} is assignable to the class of * unmodifiable collections. It is commonly used to identify whether a given class type * indicates a collection that cannot be modified (e.g., collections wrapped with * {@link Collections#unmodifiableCollection(Collection)} or its specialized variants). *

* *

Null Handling: If {@code targetType} is {@code null}, this method * will throw a {@link NullPointerException} with a clear error message.

* * @param targetType the {@link Class} to check, must not be {@code null} * @return {@code true} if the specified {@code targetType} indicates an unmodifiable collection; * {@code false} otherwise * @throws NullPointerException if {@code targetType} is {@code null} * @see Collections#unmodifiableCollection(Collection) * @see Collections#unmodifiableList(List) * @see Collections#unmodifiableSet(Set) */ public static boolean isUnmodifiable(Class targetType) { Objects.requireNonNull(targetType, "targetType (Class) cannot be null"); return unmodifiableCollectionClass.isAssignableFrom(targetType); } /** * Determines whether the specified class represents a synchronized collection type. *

* This method checks if the provided {@code targetType} is assignable to the class of * synchronized collections. It is commonly used to identify whether a given class type * indicates a collection that supports concurrent access (e.g., collections wrapped with * {@link Collections#synchronizedCollection(Collection)} or its specialized variants). *

* *

Null Handling: If {@code targetType} is {@code null}, this method * will throw a {@link NullPointerException} with a clear error message.

* * @param targetType the {@link Class} to check, must not be {@code null} * @return {@code true} if the specified {@code targetType} indicates a synchronized collection; * {@code false} otherwise * @throws NullPointerException if {@code targetType} is {@code null} * @see Collections#synchronizedCollection(Collection) * @see Collections#synchronizedList(List) * @see Collections#synchronizedSet(Set) */ public static boolean isSynchronized(Class targetType) { Objects.requireNonNull(targetType, "targetType (Class) cannot be null"); return synchronizedCollectionClass.isAssignableFrom(targetType); } /** * Wraps the provided collection in an unmodifiable wrapper appropriate to its runtime type. *

* This method ensures that the collection cannot be modified by any client code and applies the * most specific unmodifiable wrapper based on the runtime type of the provided collection: *

*
    *
  • If the collection is a {@link NavigableSet}, it is wrapped using * {@link Collections#unmodifiableNavigableSet(NavigableSet)}.
  • *
  • If the collection is a {@link SortedSet}, it is wrapped using * {@link Collections#unmodifiableSortedSet(SortedSet)}.
  • *
  • If the collection is a {@link Set}, it is wrapped using * {@link Collections#unmodifiableSet(Set)}.
  • *
  • If the collection is a {@link List}, it is wrapped using * {@link Collections#unmodifiableList(List)}.
  • *
  • Otherwise, it is wrapped using {@link Collections#unmodifiableCollection(Collection)}.
  • *
* *

* Attempting to modify the returned collection will result in an * {@link UnsupportedOperationException} at runtime. For example: *

*
{@code
     * NavigableSet set = new TreeSet<>(Set.of("A", "B", "C"));
     * NavigableSet unmodifiableSet = (NavigableSet) getUnmodifiableCollection(set);
     * unmodifiableSet.add("D"); // Throws UnsupportedOperationException
     * }
* *

Null Handling

*

* If the input collection is {@code null}, this method will throw a {@link NullPointerException} * with a descriptive error message. *

* * @param the type of elements in the collection * @param collection the collection to be wrapped in an unmodifiable wrapper * @return an unmodifiable view of the provided collection, preserving its runtime type * @throws NullPointerException if the provided collection is {@code null} * @see Collections#unmodifiableNavigableSet(NavigableSet) * @see Collections#unmodifiableSortedSet(SortedSet) * @see Collections#unmodifiableSet(Set) * @see Collections#unmodifiableList(List) * @see Collections#unmodifiableCollection(Collection) */ public static Collection getUnmodifiableCollection(Collection collection) { Objects.requireNonNull(collection, "Collection must not be null"); if (collection instanceof NavigableSet) { return Collections.unmodifiableNavigableSet((NavigableSet) collection); } else if (collection instanceof SortedSet) { return Collections.unmodifiableSortedSet((SortedSet) collection); } else if (collection instanceof Set) { return Collections.unmodifiableSet((Set) collection); } else if (collection instanceof List) { return Collections.unmodifiableList((List) collection); } else { return Collections.unmodifiableCollection(collection); } } /** * Returns an empty collection of the same type as the provided collection. *

* This method determines the runtime type of the input collection and returns an * appropriate empty collection instance: *

*
    *
  • If the collection is a {@link NavigableSet}, it returns {@link Collections#emptyNavigableSet()}.
  • *
  • If the collection is a {@link SortedSet}, it returns {@link Collections#emptySortedSet()}.
  • *
  • If the collection is a {@link Set}, it returns {@link Collections#emptySet()}.
  • *
  • If the collection is a {@link List}, it returns {@link Collections#emptyList()}.
  • *
  • For all other collection types, it defaults to returning {@link Collections#emptyList()}.
  • *
* *

* The returned collection is immutable and will throw an {@link UnsupportedOperationException} * if any modification is attempted. For example: *

*
{@code
     * List list = new ArrayList<>();
     * Collection emptyList = getEmptyCollection(list);
     *
     * emptyList.add("one"); // Throws UnsupportedOperationException
     * }
* *

Null Handling

*

* If the input collection is {@code null}, this method will throw a {@link NullPointerException} * with a descriptive error message. *

* *

Usage Notes

*
    *
  • The returned collection is type-specific based on the input collection, ensuring * compatibility with type-specific operations such as iteration or ordering.
  • *
  • The method provides an empty collection that is appropriate for APIs requiring * non-null collections as inputs or defaults.
  • *
* * @param the type of elements in the collection * @param collection the collection whose type determines the type of the returned empty collection * @return an empty, immutable collection of the same type as the input collection * @throws NullPointerException if the provided collection is {@code null} * @see Collections#emptyNavigableSet() * @see Collections#emptySortedSet() * @see Collections#emptySet() * @see Collections#emptyList() */ public static Collection getEmptyCollection(Collection collection) { Objects.requireNonNull(collection, "Collection must not be null"); if (collection instanceof NavigableSet) { return Collections.emptyNavigableSet(); } else if (collection instanceof SortedSet) { return Collections.emptySortedSet(); } else if (collection instanceof Set) { return Collections.emptySet(); } else if (collection instanceof List) { return Collections.emptyList(); } else { return Collections.emptyList(); // Default to an empty list for other collection types } } /** * Wraps the provided collection in a checked wrapper that enforces type safety. *

* This method applies the most specific checked wrapper based on the runtime type of the collection: *

*
    *
  • If the collection is a {@link NavigableSet}, it is wrapped using * {@link Collections#checkedNavigableSet(NavigableSet, Class)}.
  • *
  • If the collection is a {@link SortedSet}, it is wrapped using * {@link Collections#checkedSortedSet(SortedSet, Class)}.
  • *
  • If the collection is a {@link Set}, it is wrapped using * {@link Collections#checkedSet(Set, Class)}.
  • *
  • If the collection is a {@link List}, it is wrapped using * {@link Collections#checkedList(List, Class)}.
  • *
  • Otherwise, it is wrapped using {@link Collections#checkedCollection(Collection, Class)}.
  • *
* *

* Attempting to add an element to the returned collection that is not of the specified type * will result in a {@link ClassCastException} at runtime. For example: *

*
{@code
     * List list = new ArrayList<>(List.of("one", "two"));
     * Collection checkedCollection = getCheckedCollection(list, String.class);
     *
     * // Adding a String is allowed
     * checkedCollection.add("three");
     *
     * // Adding an Integer will throw a ClassCastException
     * checkedCollection.add(42); // Throws ClassCastException
     * }
     *
     * 

Null Handling

*

* If the input collection or the type class is {@code null}, this method will throw a * {@link NullPointerException} with a descriptive error message. *

* *

Usage Notes

*
    *
  • The method enforces runtime type safety by validating all elements added to the collection.
  • *
  • The returned collection retains the original type-specific behavior of the input collection * (e.g., sorting for {@link SortedSet} or ordering for {@link List}).
  • *
  • Use this method when you need to ensure that a collection only contains elements of a specific type.
  • *
* * @param the type of the input collection * @param the type of elements in the collection * @param collection the collection to be wrapped, must not be {@code null} * @param type the class of elements that the collection is permitted to hold, must not be {@code null} * @return a checked view of the provided collection * @throws NullPointerException if the provided collection or type is {@code null} * @see Collections#checkedNavigableSet(NavigableSet, Class) * @see Collections#checkedSortedSet(SortedSet, Class) * @see Collections#checkedSet(Set, Class) * @see Collections#checkedList(List, Class) * @see Collections#checkedCollection(Collection, Class) */ @SuppressWarnings("unchecked") public static , E> Collection getCheckedCollection(T collection, Class type) { Objects.requireNonNull(collection, "Collection must not be null"); Objects.requireNonNull(type, "Type (Class) must not be null"); if (collection instanceof NavigableSet) { return Collections.checkedNavigableSet((NavigableSet) collection, type); } else if (collection instanceof SortedSet) { return Collections.checkedSortedSet((SortedSet) collection, type); } else if (collection instanceof Set) { return Collections.checkedSet((Set) collection, type); } else if (collection instanceof List) { return Collections.checkedList((List) collection, type); } else { return Collections.checkedCollection((Collection) collection, type); } } /** * Wraps the provided collection in a thread-safe synchronized wrapper. *

* This method applies the most specific synchronized wrapper based on the runtime type of the collection: *

*
    *
  • If the collection is a {@link NavigableSet}, it is wrapped using * {@link Collections#synchronizedNavigableSet(NavigableSet)}.
  • *
  • If the collection is a {@link SortedSet}, it is wrapped using * {@link Collections#synchronizedSortedSet(SortedSet)}.
  • *
  • If the collection is a {@link Set}, it is wrapped using * {@link Collections#synchronizedSet(Set)}.
  • *
  • If the collection is a {@link List}, it is wrapped using * {@link Collections#synchronizedList(List)}.
  • *
  • Otherwise, it is wrapped using {@link Collections#synchronizedCollection(Collection)}.
  • *
* *

* The returned collection is thread-safe. However, iteration over the collection must be manually synchronized: *

*
{@code
     * List list = new ArrayList<>(List.of("one", "two", "three"));
     * Collection synchronizedList = getSynchronizedCollection(list);
     *
     * synchronized (synchronizedList) {
     *     for (String item : synchronizedList) {
     *         System.out.println(item);
     *     }
     * }
     * }
* *

Null Handling

*

* If the input collection is {@code null}, this method will throw a {@link NullPointerException} * with a descriptive error message. *

* *

Usage Notes

*
    *
  • The method returns a synchronized wrapper that delegates all operations to the original collection.
  • *
  • Any structural modifications (e.g., {@code add}, {@code remove}) must occur within a synchronized block * to ensure thread safety during concurrent access.
  • *
* * @param the type of elements in the collection * @param collection the collection to be wrapped in a synchronized wrapper * @return a synchronized view of the provided collection, preserving its runtime type * @throws NullPointerException if the provided collection is {@code null} * @see Collections#synchronizedNavigableSet(NavigableSet) * @see Collections#synchronizedSortedSet(SortedSet) * @see Collections#synchronizedSet(Set) * @see Collections#synchronizedList(List) * @see Collections#synchronizedCollection(Collection) */ public static Collection getSynchronizedCollection(Collection collection) { Objects.requireNonNull(collection, "Collection must not be null"); if (collection instanceof NavigableSet) { return Collections.synchronizedNavigableSet((NavigableSet) collection); } else if (collection instanceof SortedSet) { return Collections.synchronizedSortedSet((SortedSet) collection); } else if (collection instanceof Set) { return Collections.synchronizedSet((Set) collection); } else if (collection instanceof List) { return Collections.synchronizedList((List) collection); } else { return Collections.synchronizedCollection(collection); } } }