/*
* The MIT License (MIT)
*
* Copyright (c) 2016 Lachlan Dowding
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package permafrost.tundra.collection;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* A collection of convenience methods for working with java.util.List objects.
*/
public final class ListHelper {
/**
* Disallow instantiation of this class.
*/
private ListHelper() {}
/**
* Appends the given items to the given list.
*
* @param list The list to append the items to.
* @param items The items to be added to the list.
* @param <E> The component type of the list.
* @return The given list.
*/
public static <E> List<E> append(List<E> list, E ... items) {
return append(list, false, items);
}
/**
* Appends the given items to the given list.
*
* @param list The list to append the items to.
* @param includeNulls If true, null values will be appended, otherwise they will not.
* @param items The items to be added to the list.
* @param <E> The component type of the list.
* @return The given list.
*/
public static <E> List<E> append(List<E> list, boolean includeNulls, E ... items) {
return append(list, calculateMinimumCapacity(list, items), includeNulls, items);
}
/**
* Appends the given items to the given list.
*
* @param list The list to append the items to.
* @param minCapacity The minimum capacity the list should have before adding the items.
* @param items The items to be added to the list.
* @param <E> The component type of the list.
* @return The given list.
*/
public static <E> List<E> append(List<E> list, int minCapacity, E ... items) {
return append(list, minCapacity, false, items);
}
/**
* Appends the given items to the given list.
*
* @param list The list to append the items to.
* @param minCapacity The minimum capacity the list should have before adding the items.
* @param includeNulls If true, null values will be appended, otherwise they will not.
* @param items The items to be added to the list.
* @param <E> The component type of the list.
* @return The given list.
*/
@SuppressWarnings("unchecked")
public static <E> List<E> append(List<E> list, int minCapacity, boolean includeNulls, E ... items) {
list = (List)CollectionHelper.append(createOrGrow(list, minCapacity), includeNulls, items);
return list;
}
/**
* Returns the minimum capacity required of a list to hold the given items.
*
* @param list The list whose minimum capacity is to be calculated.
* @param items The items to be added to the list.
* @param <E> The component type of the list.
* @return The minimum capacity required of the given list to hold the given items.
*/
private static <E> int calculateMinimumCapacity(List<E> list, E ... items) {
return calculateMinimumCapacity(list, 0, items);
}
/**
* Returns the minimum capacity required of a list to hold the given items.
*
* @param list The list whose minimum capacity is to be calculated.
* @param index The index at which the items will be inserted.
* @param items The items to be added to the list.
* @param <E> The component type of the list.
* @return The minimum capacity required of the given list to hold the given items.
*/
private static <E> int calculateMinimumCapacity(List<E> list, int index, E ... items) {
int minCapacity = 0;
if (items != null && list != null) {
// support reverse/tail indexing
if (index < 0) {
index = Math.abs(index) - 1;
}
if (index > list.size()) {
minCapacity = index + items.length;
} else {
minCapacity = list.size() + items.length;
}
}
return minCapacity;
}
/**
* Removes all items from the given list, or returns a new list if the given list is null.
*
* @param list The list to be cleared.
* @param klass The component type of the list and resulting array.
* @param <E> The component type of the list.
* @return The cleared list.
*/
@SuppressWarnings("unchecked")
public static <E> List<E> clear(List<E> list, Class<E> klass) {
if (list == null) return null;
return (List<E>)CollectionHelper.clear(list);
}
/**
* Creates a new list.
*
* @param <E> The component type of the list.
* @return A new list.
*/
public static <E> List<E> create() {
return new ArrayList<E>();
}
/**
* Creates a new list with the given initial capacity.
*
* @param initialCapacity The initial capacity of the new list.
* @param <E> The component type of the list.
* @return A new list.
*/
public static <E> List<E> create(int initialCapacity) {
return new ArrayList<E>(initialCapacity);
}
/**
* Grows the given list to the given capacity, or creates a new list if the given list is null.
*
* @param list The list to be grown, or null.
* @param minCapacity The capacity to grow the list to.
* @param <E> The component type of the list.
* @return Either the given list grown to the given capacity, or a new list.
*/
private static <E> List<E> createOrGrow(List<E> list, int minCapacity) {
if (list == null) {
list = create(minCapacity);
} else if (list instanceof ArrayList) {
((ArrayList)list).ensureCapacity(minCapacity);
}
return list;
}
/**
* Prepends the given items to the front of the given list, shifting the existing items if any to the right.
*
* @param list The list to insert the items into.
* @param items The items to be added to the list.
* @param <E> The component type of the list.
* @return The given list.
*/
public static <E> List<E> prepend(List<E> list, E ...items) {
return prepend(list, false, items);
}
/**
* Prepends the given items to the front of the given list, shifting the existing items if any to the right.
*
* @param list The list to insert the items into.
* @param includeNulls If true, null values will be appended, otherwise they will not.
* @param items The items to be added to the list.
* @param <E> The component type of the list.
* @return The given list.
*/
public static <E> List<E> prepend(List<E> list, boolean includeNulls, E ...items) {
return prepend(list, calculateMinimumCapacity(list, items), includeNulls, items);
}
/**
* Prepends the given items to the front of the given list, shifting the existing items if any to the right.
*
* @param list The list to insert the items into.
* @param minCapacity The minimum capacity the list should have before adding the items.
* @param items The items to be added to the list.
* @param <E> The component type of the list.
* @return The given list.
*/
public static <E> List<E> prepend(List<E> list, int minCapacity, E ...items) {
return prepend(list, minCapacity, false, items);
}
/**
* Prepends the given items to the front of the given list, shifting the existing items if any to the right.
*
* @param list The list to insert the items into.
* @param minCapacity The minimum capacity the list should have before adding the items.
* @param includeNulls If true, null values will be appended, otherwise they will not.
* @param items The items to be added to the list.
* @param <E> The component type of the list.
* @return The given list.
*/
public static <E> List<E> prepend(List<E> list, int minCapacity, boolean includeNulls, E ...items) {
return insert(list, minCapacity, includeNulls, 0, items);
}
/**
* Inserts the given items at the given index, shifting the existing items if any to the right.
*
* @param list The list to insert the items into.
* @param index The index at which the items will be inserted.
* @param items The items to be added to the list.
* @param <E> The component type of the list.
* @return The given list.
*/
public static <E> List<E> insert(List<E> list, int index, E ...items) {
return insert(list, false, index, items);
}
/**
* Inserts the given items at the given index, shifting the existing items if any to the right.
*
* @param list The list to insert the items into.
* @param includeNulls If true, null values will be appended, otherwise they will not.
* @param index The index at which the items will be inserted.
* @param items The items to be added to the list.
* @param <E> The component type of the list.
* @return The given list.
*/
public static <E> List<E> insert(List<E> list, boolean includeNulls, int index, E ...items) {
return insert(list, calculateMinimumCapacity(list, index, items), includeNulls, index, items);
}
/**
* Inserts the given items at the given index, shifting the existing items if any to the right.
*
* @param list The list to insert the items into.
* @param minCapacity The minimum capacity the list should have before adding the items.
* @param index The index at which the items will be inserted.
* @param items The items to be added to the list.
* @param <E> The component type of the list.
* @return The given list.
*/
public static <E> List<E> insert(List<E> list, int minCapacity, int index, E ...items) {
return insert(list, minCapacity, false, index, items);
}
/**
* Inserts the given items at the given index, shifting the existing items if any to the right.
*
* @param list The list to insert the items into.
* @param minCapacity The minimum capacity the list should have before adding the items.
* @param includeNulls If true, null values will be appended, otherwise they will not.
* @param index The index at which the items will be inserted.
* @param items The items to be added to the list.
* @param <E> The component type of the list.
* @return The given list.
*/
public static <E> List<E> insert(List<E> list, int minCapacity, boolean includeNulls, int index, E ...items) {
list = createOrGrow(list, minCapacity);
if (items != null) {
// support reverse/tail indexing
if (index < 0) index += list.size() + 1;
int capacity, fillIndex;
if (index < 0) {
capacity = Math.abs(index) + list.size() + items.length - 1;
index = fillIndex = 0;
} else {
capacity = index;
fillIndex = list.size();
}
if (capacity > list.size()) {
// fill the list with nulls if it needs to be extended
for (int i = list.size(); i < capacity; i++) {
list.add(fillIndex, null);
}
}
for (E item : items) {
if (includeNulls || item != null) {
list.add(index++, item);
}
}
}
return list;
}
/**
* Converts the given array to a list.
*
* @param array The array to be converted.
* @param klass The component type of the list and resulting array.
* @param <E> The component type of the array and resulting list.
* @return A list representation of the given array.
*/
public static <E> List<E> listify(E[] array, Class<E> klass) {
if (array == null) return null;
List<E> list = create(array.length);
return append(list, array);
}
/**
* Returns true if an item exists at the given index in the given list.
*
* @param list The list to check item existence in.
* @param index The index to check item existence of.
* @param <E> The component type of the list.
* @return True if an item exists at the given index in the given list.
*/
public static <E> boolean exists(List<E> list, int index) {
if (list == null) return false;
// support negative/reverse indexing
if (index < 0) index += list.size();
return index >= 0 && index < list.size();
}
/**
* Returns the item at the given index from the given list.
*
* @param list The list to get the item from.
* @param index The index of the item to return.
* @param <E> The component type of the list.
* @return The item at the given index in the given list, or null if the list is null or the item does not
* exist.
*/
public static <E> E get(List<E> list, int index) {
if (list == null) throw new NullPointerException("list must not be null");
// support negative/reverse indexing
if (index < 0) index += list.size();
return list.get(index);
}
/**
* Replaces the value of the item at the given index in the given list.
*
* @param list The list to replace a value in.
* @param index The index of the item whose value is to be replaced.
* @param item The new value for the item.
* @param <E> The component type of the list.
* @return The previous value of the item at the given index.
*/
public static <E> E set(List<E> list, int index, E item) {
if (list == null) throw new NullPointerException("list must not be null");
// support negative/reverse indexing
if (index < 0) index += list.size();
return list.set(index, item);
}
/**
* Removes the item at the given index in the given list.
*
* @param list The list to remove the item from.
* @param index The index of the item to be removed.
* @param <E> The component type of the list.
* @return The item previously at the given index.
*/
public static <E> E remove(List<E> list, int index) {
if (list == null) throw new NullPointerException("list must not be null");
// support negative/reverse indexing
if (index < 0) index += list.size();
return list.remove(index);
}
/**
* Removes and returns the given count of items from the head of the given list.
*
* @param list The list to take the items from.
* @param count The number of items to take from the list.
* @param <E> The component type of the list.
* @return A new list containing the items taken from the head of the given list.
*/
public static <E> List<E> take(List<E> list, int count) {
if (list == null) return null;
if (count < 0) throw new IllegalArgumentException("count must not be negative");
List<E> head = create(count);
while(head.size() < count && list.size() > 0) {
head.add(list.remove(0));
}
return head;
}
/**
* Returns a new list with the items in the given list in reverse order.
*
* @param list The list to be reversed.
* @param <E> The component type of the list.
* @return The reverse of the given list.
*/
public static <E> List<E> reverse(List<E> list) {
if (list == null) return null;
List<E> reverseList = create();
for (int i = list.size() - 1; i >= 0; i--) {
reverseList.add(list.get(i));
}
return reverseList;
}
/**
* Returns a set intersection of the given lists.
*
* @param lists The lists to find the set intersection of.
* @param <E> The component type of the lists.
* @return The set intersection of the given lists.
*/
public static <E> List<E> intersect(List<E> ... lists) {
List<E> results = create();
boolean seeded = false;
for (List<E> list : lists) {
if (list != null) {
if (seeded) {
results.retainAll(list);
} else {
results.addAll(list);
seeded = true;
}
}
}
return results;
}
/**
* Returns the set difference of the given lists.
*
* @param firstList The list to be subtracted from.
* @param secondList The list subtracted from the first list.
* @param <E> The component type of the lists.
* @return The set difference of the two lists.
*/
public static <E> List<E> difference(List<E> firstList, List<E> secondList) {
List<E> results = create();
if (firstList != null) results.addAll(firstList);
if (secondList != null) results.removeAll(secondList);
return results;
}
/**
* Returns a new list with all duplicate items removed.
*
* @param list A list.
* @param <E> The component type of the list.
* @return A new list containing only the unique items from the given list.
*/
public static <E> List<E> unique(List<E> list) {
if (list == null) return null;
Set<E> set = new LinkedHashSet<E>();
if (list != null) {
set.addAll(list);
}
List<E> results = create(set.size());
results.addAll(set);
return results;
}
}