This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-collections.git
commit 90d2a9bcee6768e0c18ed1b9913c70aef8ff12d6 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Mon Aug 2 13:23:50 2021 -0400 Sort members. --- .../org/apache/commons/collections4/ListUtils.java | 948 ++++++++++----------- .../apache/commons/collections4/ListUtilsTest.java | 390 ++++----- 2 files changed, 669 insertions(+), 669 deletions(-) diff --git a/src/main/java/org/apache/commons/collections4/ListUtils.java b/src/main/java/org/apache/commons/collections4/ListUtils.java index 548bf14..e8c3d8a 100644 --- a/src/main/java/org/apache/commons/collections4/ListUtils.java +++ b/src/main/java/org/apache/commons/collections4/ListUtils.java @@ -43,21 +43,94 @@ import org.apache.commons.collections4.sequence.SequencesComparator; */ public class ListUtils { /** - * Don't allow instances. + * A simple wrapper to use a CharSequence as List. */ - private ListUtils() {} + private static final class CharSequenceAsList extends AbstractList<Character> { + private final CharSequence sequence; + + CharSequenceAsList(final CharSequence sequence) { + this.sequence = sequence; + } + + @Override + public Character get(final int index) { + return Character.valueOf(sequence.charAt(index)); + } + + @Override + public int size() { + return sequence.length(); + } + } /** - * Returns an immutable empty list if the argument is {@code null}, - * or the argument itself otherwise. - * - * @param <T> the element type - * @param list the list, possibly {@code null} - * @return an empty list if the argument is {@code null} + * A helper class used to construct the longest common subsequence. */ - public static <T> List<T> emptyIfNull(final List<T> list) { - return list == null ? Collections.<T>emptyList() : list; + private static final class LcsVisitor<E> implements CommandVisitor<E> { + private final ArrayList<E> sequence; + + LcsVisitor() { + sequence = new ArrayList<>(); + } + + public List<E> getSubSequence() { + return sequence; + } + + @Override + public void visitDeleteCommand(final E object) { + // noop + } + + @Override + public void visitInsertCommand(final E object) { + // noop + } + + @Override + public void visitKeepCommand(final E object) { + sequence.add(object); + } + } + + /** + * Provides a partition view on a {@link List}. + * @since 4.0 + */ + private static class Partition<T> extends AbstractList<List<T>> { + private final List<T> list; + private final int size; + + private Partition(final List<T> list, final int size) { + this.list = list; + this.size = size; + } + + @Override + public List<T> get(final int index) { + final int listSize = size(); + if (index < 0) { + throw new IndexOutOfBoundsException("Index " + index + " must not be negative"); + } + if (index >= listSize) { + throw new IndexOutOfBoundsException("Index " + index + " must be less than size " + + listSize); + } + final int start = index * size; + final int end = Math.min(start + size, list.size()); + return list.subList(start, end); + } + + @Override + public boolean isEmpty() { + return list.isEmpty(); + } + + @Override + public int size() { + return (int) Math.ceil((double) list.size() / (double) size); + } } /** @@ -75,6 +148,82 @@ public class ListUtils { } /** + * Returns an immutable empty list if the argument is {@code null}, + * or the argument itself otherwise. + * + * @param <T> the element type + * @param list the list, possibly {@code null} + * @return an empty list if the argument is {@code null} + */ + public static <T> List<T> emptyIfNull(final List<T> list) { + return list == null ? Collections.<T>emptyList() : list; + } + + /** + * Returns a fixed-sized list backed by the given list. + * Elements may not be added or removed from the returned list, but + * existing elements can be changed (for instance, via the + * {@link List#set(int, Object)} method). + * + * @param <E> the element type + * @param list the list whose size to fix, must not be null + * @return a fixed-size list backed by that list + * @throws NullPointerException if the List is null + */ + public static <E> List<E> fixedSizeList(final List<E> list) { + return FixedSizeList.fixedSizeList(list); + } + + /** + * Generates a hash code using the algorithm specified in + * {@link java.util.List#hashCode()}. + * <p> + * This method is useful for implementing {@code List} when you cannot + * extend AbstractList. The method takes Collection instances to enable other + * collection types to use the List implementation algorithm. + * + * @see java.util.List#hashCode() + * @param list the list to generate the hashCode for, may be null + * @return the hash code + */ + public static int hashCodeForList(final Collection<?> list) { + if (list == null) { + return 0; + } + int hashCode = 1; + final Iterator<?> it = list.iterator(); + + while (it.hasNext()) { + final Object obj = it.next(); + hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode()); + } + return hashCode; + } + + /** + * Finds the first index in the given List which matches the given predicate. + * <p> + * If the input List or predicate is null, or no element of the List + * matches the predicate, -1 is returned. + * + * @param <E> the element type + * @param list the List to search, may be null + * @param predicate the predicate to use, may be null + * @return the first index of an Object in the List which matches the predicate or -1 if none could be found + */ + public static <E> int indexOf(final List<E> list, final Predicate<E> predicate) { + if (list != null && predicate != null) { + for (int i = 0; i < list.size(); i++) { + final E item = list.get(i); + if (predicate.evaluate(item)) { + return i; + } + } + } + return CollectionUtils.INDEX_NOT_FOUND; + } + + /** * Returns a new list containing all elements that are contained in * both given lists. * @@ -106,215 +255,232 @@ public class ListUtils { } /** - * Subtracts all elements in the second list from the first list, - * placing the results in a new list. + * Tests two lists for value-equality as per the equality contract in + * {@link java.util.List#equals(java.lang.Object)}. * <p> - * This differs from {@link List#removeAll(Collection)} in that - * cardinality is respected; if <Code>list1</Code> contains two - * occurrences of <Code>null</Code> and <Code>list2</Code> only - * contains one occurrence, then the returned list will still contain - * one occurrence. + * This method is useful for implementing {@code List} when you cannot + * extend AbstractList. The method takes Collection instances to enable other + * collection types to use the List implementation algorithm. + * <p> + * The relevant text (slightly paraphrased as this is a static method) is: + * <blockquote> + * Compares the two list objects for equality. Returns + * {@code true} if and only if both + * lists have the same size, and all corresponding pairs of elements in + * the two lists are <i>equal</i>. (Two elements {@code e1} and + * {@code e2} are <i>equal</i> if <code>(e1==null ? e2==null : + * e1.equals(e2))</code>.) In other words, two lists are defined to be + * equal if they contain the same elements in the same order. This + * definition ensures that the equals method works properly across + * different implementations of the {@code List} interface. + * </blockquote> * - * @param <E> the element type - * @param list1 the list to subtract from - * @param list2 the list to subtract - * @return a new list containing the results - * @throws NullPointerException if either list is null + * <b>Note:</b> The behavior of this method is undefined if the lists are + * modified during the equals comparison. + * + * @see java.util.List + * @param list1 the first list, may be null + * @param list2 the second list, may be null + * @return whether the lists are equal by value comparison */ - public static <E> List<E> subtract(final List<E> list1, final List<? extends E> list2) { - final ArrayList<E> result = new ArrayList<>(); - final HashBag<E> bag = new HashBag<>(list2); - for (final E e : list1) { - if (!bag.remove(e, 1)) { - result.add(e); + public static boolean isEqualList(final Collection<?> list1, final Collection<?> list2) { + if (list1 == list2) { + return true; + } + if (list1 == null || list2 == null || list1.size() != list2.size()) { + return false; + } + + final Iterator<?> it1 = list1.iterator(); + final Iterator<?> it2 = list2.iterator(); + Object obj1 = null; + Object obj2 = null; + + while (it1.hasNext() && it2.hasNext()) { + obj1 = it1.next(); + obj2 = it2.next(); + + if (!(obj1 == null ? obj2 == null : obj1.equals(obj2))) { + return false; } } - return result; - } - /** - * Returns the sum of the given lists. This is their intersection - * subtracted from their union. - * - * @param <E> the element type - * @param list1 the first list - * @param list2 the second list - * @return a new list containing the sum of those lists - * @throws NullPointerException if either list is null - */ - public static <E> List<E> sum(final List<? extends E> list1, final List<? extends E> list2) { - return subtract(union(list1, list2), intersection(list1, list2)); + return !(it1.hasNext() || it2.hasNext()); } /** - * Returns a new list containing the second list appended to the - * first list. The {@link List#addAll(Collection)} operation is - * used to append the two given lists into a new list. + * Returns a "lazy" list whose elements will be created on demand. + * <p> + * When the index passed to the returned list's {@link List#get(int) get} + * method is greater than the list's size, then the factory will be used + * to create a new object and that object will be inserted at that index. + * <p> + * For instance: + * + * <pre> + * Factory<Date> factory = new Factory<Date>() { + * public Date create() { + * return new Date(); + * } + * } + * List<Date> lazy = ListUtils.lazyList(new ArrayList<Date>(), factory); + * Date date = lazy.get(3); + * </pre> + * + * After the above code is executed, {@code date} will refer to + * a new {@code Date} instance. Furthermore, that {@code Date} + * instance is the fourth element in the list. The first, second, + * and third element are all set to {@code null}. * * @param <E> the element type - * @param list1 the first list - * @param list2 the second list - * @return a new list containing the union of those lists - * @throws NullPointerException if either list is null + * @param list the list to make lazy, must not be null + * @param factory the factory for creating new objects, must not be null + * @return a lazy list backed by the given list + * @throws NullPointerException if the List or Factory is null */ - public static <E> List<E> union(final List<? extends E> list1, final List<? extends E> list2) { - final ArrayList<E> result = new ArrayList<>(list1.size() + list2.size()); - result.addAll(list1); - result.addAll(list2); - return result; + public static <E> List<E> lazyList(final List<E> list, final Factory<? extends E> factory) { + return LazyList.lazyList(list, factory); } /** - * Selects all elements from input collection which match the given - * predicate into an output list. + * Returns a "lazy" list whose elements will be created on demand. * <p> - * A {@code null} predicate matches no elements. + * When the index passed to the returned list's {@link List#get(int) get} + * method is greater than the list's size, then the transformer will be used + * to create a new object and that object will be inserted at that index. + * <p> + * For instance: * - * @param <E> the element type - * @param inputCollection the collection to get the input from, may not be null - * @param predicate the predicate to use, may be null - * @return the elements matching the predicate (new list) - * @throws NullPointerException if the input list is null + * <pre> + * List<Integer> hours = Arrays.asList(7, 5, 8, 2); + * Transformer<Integer,Date> transformer = input -> LocalDateTime.now().withHour(hours.get(input)); + * List<LocalDateTime> lazy = ListUtils.lazyList(new ArrayList<LocalDateTime>(), transformer); + * Date date = lazy.get(3); + * </pre> * - * @since 4.0 - * @see CollectionUtils#select(Iterable, Predicate) + * After the above code is executed, {@code date} will refer to + * a new {@code Date} instance. Furthermore, that {@code Date} + * instance is the fourth element in the list. The first, second, + * and third element are all set to {@code null}. + * + * @param <E> the element type + * @param list the list to make lazy, must not be null + * @param transformer the transformer for creating new objects, must not be null + * @return a lazy list backed by the given list + * @throws NullPointerException if the List or Transformer is null */ - public static <E> List<E> select(final Collection<? extends E> inputCollection, - final Predicate<? super E> predicate) { - return CollectionUtils.select(inputCollection, predicate, new ArrayList<E>(inputCollection.size())); + public static <E> List<E> lazyList(final List<E> list, final Transformer<Integer, ? extends E> transformer) { + return LazyList.lazyList(list, transformer); } /** - * Selects all elements from inputCollection which don't match the given - * predicate into an output collection. + * Returns the longest common subsequence (LCS) of two {@link CharSequence} objects. * <p> - * If the input predicate is {@code null}, the result is an empty list. - * - * @param <E> the element type - * @param inputCollection the collection to get the input from, may not be null - * @param predicate the predicate to use, may be null - * @return the elements <b>not</b> matching the predicate (new list) - * @throws NullPointerException if the input collection is null + * This is a convenience method for using {@link #longestCommonSubsequence(List, List)} + * with {@link CharSequence} instances. * + * @param charSequenceA the first sequence + * @param charSequenceB the second sequence + * @return the longest common subsequence as {@link String} + * @throws NullPointerException if either sequence is {@code null} * @since 4.0 - * @see CollectionUtils#selectRejected(Iterable, Predicate) */ - public static <E> List<E> selectRejected(final Collection<? extends E> inputCollection, - final Predicate<? super E> predicate) { - return CollectionUtils.selectRejected(inputCollection, predicate, new ArrayList<E>(inputCollection.size())); + public static String longestCommonSubsequence(final CharSequence charSequenceA, final CharSequence charSequenceB) { + Objects.requireNonNull(charSequenceA, "charSequenceA"); + Objects.requireNonNull(charSequenceB, "charSequenceB"); + final List<Character> lcs = longestCommonSubsequence(new CharSequenceAsList(charSequenceA), + new CharSequenceAsList(charSequenceB)); + final StringBuilder sb = new StringBuilder(); + for (final Character ch : lcs) { + sb.append(ch); + } + return sb.toString(); } /** - * Tests two lists for value-equality as per the equality contract in - * {@link java.util.List#equals(java.lang.Object)}. - * <p> - * This method is useful for implementing {@code List} when you cannot - * extend AbstractList. The method takes Collection instances to enable other - * collection types to use the List implementation algorithm. - * <p> - * The relevant text (slightly paraphrased as this is a static method) is: - * <blockquote> - * Compares the two list objects for equality. Returns - * {@code true} if and only if both - * lists have the same size, and all corresponding pairs of elements in - * the two lists are <i>equal</i>. (Two elements {@code e1} and - * {@code e2} are <i>equal</i> if <code>(e1==null ? e2==null : - * e1.equals(e2))</code>.) In other words, two lists are defined to be - * equal if they contain the same elements in the same order. This - * definition ensures that the equals method works properly across - * different implementations of the {@code List} interface. - * </blockquote> - * - * <b>Note:</b> The behavior of this method is undefined if the lists are - * modified during the equals comparison. + * Returns the longest common subsequence (LCS) of two sequences (lists). * - * @see java.util.List - * @param list1 the first list, may be null - * @param list2 the second list, may be null - * @return whether the lists are equal by value comparison + * @param <E> the element type + * @param a the first list + * @param b the second list + * @return the longest common subsequence + * @throws NullPointerException if either list is {@code null} + * @since 4.0 */ - public static boolean isEqualList(final Collection<?> list1, final Collection<?> list2) { - if (list1 == list2) { - return true; - } - if (list1 == null || list2 == null || list1.size() != list2.size()) { - return false; - } - - final Iterator<?> it1 = list1.iterator(); - final Iterator<?> it2 = list2.iterator(); - Object obj1 = null; - Object obj2 = null; - - while (it1.hasNext() && it2.hasNext()) { - obj1 = it1.next(); - obj2 = it2.next(); + public static <E> List<E> longestCommonSubsequence(final List<E> a, final List<E> b) { + return longestCommonSubsequence( a, b, DefaultEquator.defaultEquator() ); + } - if (!(obj1 == null ? obj2 == null : obj1.equals(obj2))) { - return false; - } - } + /** + * Returns the longest common subsequence (LCS) of two sequences (lists). + * + * @param <E> the element type + * @param listA the first list + * @param listB the second list + * @param equator the equator used to test object equality + * @return the longest common subsequence + * @throws NullPointerException if either list or the equator is {@code null} + * @since 4.0 + */ + public static <E> List<E> longestCommonSubsequence(final List<E> listA, final List<E> listB, + final Equator<? super E> equator) { + Objects.requireNonNull(listA, "listA"); + Objects.requireNonNull(listB, "listB"); + Objects.requireNonNull(equator, "equator"); - return !(it1.hasNext() || it2.hasNext()); + final SequencesComparator<E> comparator = new SequencesComparator<>(listA, listB, equator); + final EditScript<E> script = comparator.getScript(); + final LcsVisitor<E> visitor = new LcsVisitor<>(); + script.visit(visitor); + return visitor.getSubSequence(); } /** - * Generates a hash code using the algorithm specified in - * {@link java.util.List#hashCode()}. + * Returns consecutive {@link List#subList(int, int) sublists} of a + * list, each of the same size (the final list may be smaller). For example, + * partitioning a list containing {@code [a, b, c, d, e]} with a partition + * size of 3 yields {@code [[a, b, c], [d, e]]} -- an outer list containing + * two inner lists of three and two elements, all in the original order. * <p> - * This method is useful for implementing {@code List} when you cannot - * extend AbstractList. The method takes Collection instances to enable other - * collection types to use the List implementation algorithm. + * The outer list is unmodifiable, but reflects the latest state of the + * source list. The inner lists are sublist views of the original list, + * produced on demand using {@link List#subList(int, int)}, and are subject + * to all the usual caveats about modification as explained in that API. + * <p> + * Adapted from http://code.google.com/p/guava-libraries/ * - * @see java.util.List#hashCode() - * @param list the list to generate the hashCode for, may be null - * @return the hash code + * @param <T> the element type + * @param list the list to return consecutive sublists of + * @param size the desired size of each sublist (the last may be smaller) + * @return a list of consecutive sublists + * @throws NullPointerException if list is null + * @throws IllegalArgumentException if size is not strictly positive + * @since 4.0 */ - public static int hashCodeForList(final Collection<?> list) { - if (list == null) { - return 0; - } - int hashCode = 1; - final Iterator<?> it = list.iterator(); - - while (it.hasNext()) { - final Object obj = it.next(); - hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode()); + public static <T> List<List<T>> partition(final List<T> list, final int size) { + Objects.requireNonNull(list, "list"); + if (size <= 0) { + throw new IllegalArgumentException("Size must be greater than 0"); } - return hashCode; + return new Partition<>(list, size); } /** - * Returns a List containing all the elements in {@code collection} - * that are also in {@code retain}. The cardinality of an element {@code e} - * in the returned list is the same as the cardinality of {@code e} - * in {@code collection} unless {@code retain} does not contain {@code e}, in which - * case the cardinality is zero. This method is useful if you do not wish to modify - * the collection {@code c} and thus cannot call {@code collection.retainAll(retain);}. + * Returns a predicated (validating) list backed by the given list. * <p> - * This implementation iterates over {@code collection}, checking each element in - * turn to see if it's contained in {@code retain}. If it's contained, it's added - * to the returned list. As a consequence, it is advised to use a collection type for - * {@code retain} that provides a fast (e.g. O(1)) implementation of - * {@link Collection#contains(Object)}. + * Only objects that pass the test in the given predicate can be added to the list. + * Trying to add an invalid object results in an IllegalArgumentException. + * It is important not to use the original list after invoking this method, + * as it is a backdoor for adding invalid objects. * - * @param <E> the element type - * @param collection the collection whose contents are the target of the #retailAll operation - * @param retain the collection containing the elements to be retained in the returned collection - * @return a {@code List} containing all the elements of {@code c} - * that occur at least once in {@code retain}. - * @throws NullPointerException if either parameter is null - * @since 3.2 + * @param <E> the element type + * @param list the list to predicate, must not be null + * @param predicate the predicate for the list, must not be null + * @return a predicated list backed by the given list + * @throws NullPointerException if the List or Predicate is null */ - public static <E> List<E> retainAll(final Collection<E> collection, final Collection<?> retain) { - final List<E> list = new ArrayList<>(Math.min(collection.size(), retain.size())); - - for (final E obj : collection) { - if (retain.contains(obj)) { - list.add(obj); - } - } - return list; + public static <E> List<E> predicatedList(final List<E> list, final Predicate<E> predicate) { + return PredicatedList.predicatedList(list, predicate); } /** @@ -351,6 +517,120 @@ public class ListUtils { } /** + * Returns a List containing all the elements in {@code collection} + * that are also in {@code retain}. The cardinality of an element {@code e} + * in the returned list is the same as the cardinality of {@code e} + * in {@code collection} unless {@code retain} does not contain {@code e}, in which + * case the cardinality is zero. This method is useful if you do not wish to modify + * the collection {@code c} and thus cannot call {@code collection.retainAll(retain);}. + * <p> + * This implementation iterates over {@code collection}, checking each element in + * turn to see if it's contained in {@code retain}. If it's contained, it's added + * to the returned list. As a consequence, it is advised to use a collection type for + * {@code retain} that provides a fast (e.g. O(1)) implementation of + * {@link Collection#contains(Object)}. + * + * @param <E> the element type + * @param collection the collection whose contents are the target of the #retailAll operation + * @param retain the collection containing the elements to be retained in the returned collection + * @return a {@code List} containing all the elements of {@code c} + * that occur at least once in {@code retain}. + * @throws NullPointerException if either parameter is null + * @since 3.2 + */ + public static <E> List<E> retainAll(final Collection<E> collection, final Collection<?> retain) { + final List<E> list = new ArrayList<>(Math.min(collection.size(), retain.size())); + + for (final E obj : collection) { + if (retain.contains(obj)) { + list.add(obj); + } + } + return list; + } + + /** + * Selects all elements from input collection which match the given + * predicate into an output list. + * <p> + * A {@code null} predicate matches no elements. + * + * @param <E> the element type + * @param inputCollection the collection to get the input from, may not be null + * @param predicate the predicate to use, may be null + * @return the elements matching the predicate (new list) + * @throws NullPointerException if the input list is null + * + * @since 4.0 + * @see CollectionUtils#select(Iterable, Predicate) + */ + public static <E> List<E> select(final Collection<? extends E> inputCollection, + final Predicate<? super E> predicate) { + return CollectionUtils.select(inputCollection, predicate, new ArrayList<E>(inputCollection.size())); + } + + /** + * Selects all elements from inputCollection which don't match the given + * predicate into an output collection. + * <p> + * If the input predicate is {@code null}, the result is an empty list. + * + * @param <E> the element type + * @param inputCollection the collection to get the input from, may not be null + * @param predicate the predicate to use, may be null + * @return the elements <b>not</b> matching the predicate (new list) + * @throws NullPointerException if the input collection is null + * + * @since 4.0 + * @see CollectionUtils#selectRejected(Iterable, Predicate) + */ + public static <E> List<E> selectRejected(final Collection<? extends E> inputCollection, + final Predicate<? super E> predicate) { + return CollectionUtils.selectRejected(inputCollection, predicate, new ArrayList<E>(inputCollection.size())); + } + + /** + * Subtracts all elements in the second list from the first list, + * placing the results in a new list. + * <p> + * This differs from {@link List#removeAll(Collection)} in that + * cardinality is respected; if <Code>list1</Code> contains two + * occurrences of <Code>null</Code> and <Code>list2</Code> only + * contains one occurrence, then the returned list will still contain + * one occurrence. + * + * @param <E> the element type + * @param list1 the list to subtract from + * @param list2 the list to subtract + * @return a new list containing the results + * @throws NullPointerException if either list is null + */ + public static <E> List<E> subtract(final List<E> list1, final List<? extends E> list2) { + final ArrayList<E> result = new ArrayList<>(); + final HashBag<E> bag = new HashBag<>(list2); + for (final E e : list1) { + if (!bag.remove(e, 1)) { + result.add(e); + } + } + return result; + } + + /** + * Returns the sum of the given lists. This is their intersection + * subtracted from their union. + * + * @param <E> the element type + * @param list1 the first list + * @param list2 the second list + * @return a new list containing the sum of those lists + * @throws NullPointerException if either list is null + */ + public static <E> List<E> sum(final List<? extends E> list1, final List<? extends E> list2) { + return subtract(union(list1, list2), intersection(list1, list2)); + } + + /** * Returns a synchronized list backed by the given list. * <p> * You must manually synchronize on the returned list's iterator to @@ -378,38 +658,6 @@ public class ListUtils { } /** - * Returns an unmodifiable list backed by the given list. - * <p> - * This method uses the implementation in the decorators subpackage. - * - * @param <E> the element type - * @param list the list to make unmodifiable, must not be null - * @return an unmodifiable list backed by the given list - * @throws NullPointerException if the list is null - */ - public static <E> List<E> unmodifiableList(final List<? extends E> list) { - return UnmodifiableList.unmodifiableList(list); - } - - /** - * Returns a predicated (validating) list backed by the given list. - * <p> - * Only objects that pass the test in the given predicate can be added to the list. - * Trying to add an invalid object results in an IllegalArgumentException. - * It is important not to use the original list after invoking this method, - * as it is a backdoor for adding invalid objects. - * - * @param <E> the element type - * @param list the list to predicate, must not be null - * @param predicate the predicate for the list, must not be null - * @return a predicated list backed by the given list - * @throws NullPointerException if the List or Predicate is null - */ - public static <E> List<E> predicatedList(final List<E> list, final Predicate<E> predicate) { - return PredicatedList.predicatedList(list, predicate); - } - - /** * Returns a transformed list backed by the given list. * <p> * This method returns a new list (decorating the specified list) that @@ -435,287 +683,39 @@ public class ListUtils { } /** - * Returns a "lazy" list whose elements will be created on demand. - * <p> - * When the index passed to the returned list's {@link List#get(int) get} - * method is greater than the list's size, then the factory will be used - * to create a new object and that object will be inserted at that index. - * <p> - * For instance: - * - * <pre> - * Factory<Date> factory = new Factory<Date>() { - * public Date create() { - * return new Date(); - * } - * } - * List<Date> lazy = ListUtils.lazyList(new ArrayList<Date>(), factory); - * Date date = lazy.get(3); - * </pre> - * - * After the above code is executed, {@code date} will refer to - * a new {@code Date} instance. Furthermore, that {@code Date} - * instance is the fourth element in the list. The first, second, - * and third element are all set to {@code null}. - * - * @param <E> the element type - * @param list the list to make lazy, must not be null - * @param factory the factory for creating new objects, must not be null - * @return a lazy list backed by the given list - * @throws NullPointerException if the List or Factory is null - */ - public static <E> List<E> lazyList(final List<E> list, final Factory<? extends E> factory) { - return LazyList.lazyList(list, factory); - } - - /** - * Returns a "lazy" list whose elements will be created on demand. - * <p> - * When the index passed to the returned list's {@link List#get(int) get} - * method is greater than the list's size, then the transformer will be used - * to create a new object and that object will be inserted at that index. - * <p> - * For instance: - * - * <pre> - * List<Integer> hours = Arrays.asList(7, 5, 8, 2); - * Transformer<Integer,Date> transformer = input -> LocalDateTime.now().withHour(hours.get(input)); - * List<LocalDateTime> lazy = ListUtils.lazyList(new ArrayList<LocalDateTime>(), transformer); - * Date date = lazy.get(3); - * </pre> - * - * After the above code is executed, {@code date} will refer to - * a new {@code Date} instance. Furthermore, that {@code Date} - * instance is the fourth element in the list. The first, second, - * and third element are all set to {@code null}. + * Returns a new list containing the second list appended to the + * first list. The {@link List#addAll(Collection)} operation is + * used to append the two given lists into a new list. * * @param <E> the element type - * @param list the list to make lazy, must not be null - * @param transformer the transformer for creating new objects, must not be null - * @return a lazy list backed by the given list - * @throws NullPointerException if the List or Transformer is null - */ - public static <E> List<E> lazyList(final List<E> list, final Transformer<Integer, ? extends E> transformer) { - return LazyList.lazyList(list, transformer); - } - - /** - * Returns a fixed-sized list backed by the given list. - * Elements may not be added or removed from the returned list, but - * existing elements can be changed (for instance, via the - * {@link List#set(int, Object)} method). - * - * @param <E> the element type - * @param list the list whose size to fix, must not be null - * @return a fixed-size list backed by that list - * @throws NullPointerException if the List is null + * @param list1 the first list + * @param list2 the second list + * @return a new list containing the union of those lists + * @throws NullPointerException if either list is null */ - public static <E> List<E> fixedSizeList(final List<E> list) { - return FixedSizeList.fixedSizeList(list); + public static <E> List<E> union(final List<? extends E> list1, final List<? extends E> list2) { + final ArrayList<E> result = new ArrayList<>(list1.size() + list2.size()); + result.addAll(list1); + result.addAll(list2); + return result; } /** - * Finds the first index in the given List which matches the given predicate. + * Returns an unmodifiable list backed by the given list. * <p> - * If the input List or predicate is null, or no element of the List - * matches the predicate, -1 is returned. - * - * @param <E> the element type - * @param list the List to search, may be null - * @param predicate the predicate to use, may be null - * @return the first index of an Object in the List which matches the predicate or -1 if none could be found - */ - public static <E> int indexOf(final List<E> list, final Predicate<E> predicate) { - if (list != null && predicate != null) { - for (int i = 0; i < list.size(); i++) { - final E item = list.get(i); - if (predicate.evaluate(item)) { - return i; - } - } - } - return CollectionUtils.INDEX_NOT_FOUND; - } - - /** - * Returns the longest common subsequence (LCS) of two sequences (lists). - * - * @param <E> the element type - * @param a the first list - * @param b the second list - * @return the longest common subsequence - * @throws NullPointerException if either list is {@code null} - * @since 4.0 - */ - public static <E> List<E> longestCommonSubsequence(final List<E> a, final List<E> b) { - return longestCommonSubsequence( a, b, DefaultEquator.defaultEquator() ); - } - - /** - * Returns the longest common subsequence (LCS) of two sequences (lists). + * This method uses the implementation in the decorators subpackage. * * @param <E> the element type - * @param listA the first list - * @param listB the second list - * @param equator the equator used to test object equality - * @return the longest common subsequence - * @throws NullPointerException if either list or the equator is {@code null} - * @since 4.0 - */ - public static <E> List<E> longestCommonSubsequence(final List<E> listA, final List<E> listB, - final Equator<? super E> equator) { - Objects.requireNonNull(listA, "listA"); - Objects.requireNonNull(listB, "listB"); - Objects.requireNonNull(equator, "equator"); - - final SequencesComparator<E> comparator = new SequencesComparator<>(listA, listB, equator); - final EditScript<E> script = comparator.getScript(); - final LcsVisitor<E> visitor = new LcsVisitor<>(); - script.visit(visitor); - return visitor.getSubSequence(); - } - - /** - * Returns the longest common subsequence (LCS) of two {@link CharSequence} objects. - * <p> - * This is a convenience method for using {@link #longestCommonSubsequence(List, List)} - * with {@link CharSequence} instances. - * - * @param charSequenceA the first sequence - * @param charSequenceB the second sequence - * @return the longest common subsequence as {@link String} - * @throws NullPointerException if either sequence is {@code null} - * @since 4.0 - */ - public static String longestCommonSubsequence(final CharSequence charSequenceA, final CharSequence charSequenceB) { - Objects.requireNonNull(charSequenceA, "charSequenceA"); - Objects.requireNonNull(charSequenceB, "charSequenceB"); - final List<Character> lcs = longestCommonSubsequence(new CharSequenceAsList(charSequenceA), - new CharSequenceAsList(charSequenceB)); - final StringBuilder sb = new StringBuilder(); - for (final Character ch : lcs) { - sb.append(ch); - } - return sb.toString(); - } - - /** - * A helper class used to construct the longest common subsequence. - */ - private static final class LcsVisitor<E> implements CommandVisitor<E> { - private final ArrayList<E> sequence; - - LcsVisitor() { - sequence = new ArrayList<>(); - } - - @Override - public void visitInsertCommand(final E object) { - // noop - } - - @Override - public void visitDeleteCommand(final E object) { - // noop - } - - @Override - public void visitKeepCommand(final E object) { - sequence.add(object); - } - - public List<E> getSubSequence() { - return sequence; - } - } - - /** - * A simple wrapper to use a CharSequence as List. - */ - private static final class CharSequenceAsList extends AbstractList<Character> { - private final CharSequence sequence; - - CharSequenceAsList(final CharSequence sequence) { - this.sequence = sequence; - } - - @Override - public Character get(final int index) { - return Character.valueOf(sequence.charAt(index)); - } - - @Override - public int size() { - return sequence.length(); - } - } - - /** - * Returns consecutive {@link List#subList(int, int) sublists} of a - * list, each of the same size (the final list may be smaller). For example, - * partitioning a list containing {@code [a, b, c, d, e]} with a partition - * size of 3 yields {@code [[a, b, c], [d, e]]} -- an outer list containing - * two inner lists of three and two elements, all in the original order. - * <p> - * The outer list is unmodifiable, but reflects the latest state of the - * source list. The inner lists are sublist views of the original list, - * produced on demand using {@link List#subList(int, int)}, and are subject - * to all the usual caveats about modification as explained in that API. - * <p> - * Adapted from http://code.google.com/p/guava-libraries/ - * - * @param <T> the element type - * @param list the list to return consecutive sublists of - * @param size the desired size of each sublist (the last may be smaller) - * @return a list of consecutive sublists - * @throws NullPointerException if list is null - * @throws IllegalArgumentException if size is not strictly positive - * @since 4.0 + * @param list the list to make unmodifiable, must not be null + * @return an unmodifiable list backed by the given list + * @throws NullPointerException if the list is null */ - public static <T> List<List<T>> partition(final List<T> list, final int size) { - Objects.requireNonNull(list, "list"); - if (size <= 0) { - throw new IllegalArgumentException("Size must be greater than 0"); - } - return new Partition<>(list, size); + public static <E> List<E> unmodifiableList(final List<? extends E> list) { + return UnmodifiableList.unmodifiableList(list); } /** - * Provides a partition view on a {@link List}. - * @since 4.0 + * Don't allow instances. */ - private static class Partition<T> extends AbstractList<List<T>> { - private final List<T> list; - private final int size; - - private Partition(final List<T> list, final int size) { - this.list = list; - this.size = size; - } - - @Override - public List<T> get(final int index) { - final int listSize = size(); - if (index < 0) { - throw new IndexOutOfBoundsException("Index " + index + " must not be negative"); - } - if (index >= listSize) { - throw new IndexOutOfBoundsException("Index " + index + " must be less than size " + - listSize); - } - final int start = index * size; - final int end = Math.min(start + size, list.size()); - return list.subList(start, end); - } - - @Override - public int size() { - return (int) Math.ceil((double) list.size() / (double) size); - } - - @Override - public boolean isEmpty() { - return list.isEmpty(); - } - } + private ListUtils() {} } diff --git a/src/test/java/org/apache/commons/collections4/ListUtilsTest.java b/src/test/java/org/apache/commons/collections4/ListUtilsTest.java index 4ab2c6d..11ccd02 100644 --- a/src/test/java/org/apache/commons/collections4/ListUtilsTest.java +++ b/src/test/java/org/apache/commons/collections4/ListUtilsTest.java @@ -43,7 +43,9 @@ public class ListUtilsTest { private static final String e = "e"; private static final String x = "x"; + private static final Predicate<Number> EQUALS_TWO = input -> input.intValue() == 2; private String[] fullArray; + private List<String> fullList; @BeforeEach @@ -52,13 +54,69 @@ public class ListUtilsTest { fullList = new ArrayList<>(Arrays.asList(fullArray)); } + @Test + public void testDefaultIfNull() { + assertTrue(ListUtils.defaultIfNull(null, Collections.emptyList()).isEmpty()); + + final List<Long> list = new ArrayList<>(); + assertSame(list, ListUtils.defaultIfNull(list, Collections.<Long>emptyList())); + } + + @Test + public void testEmptyIfNull() { + assertTrue(ListUtils.emptyIfNull(null).isEmpty()); + + final List<Long> list = new ArrayList<>(); + assertSame(list, ListUtils.emptyIfNull(list)); + } + + @Test + public void testEquals() { + final Collection<String> data = Arrays.asList("a", "b", "c"); + + final List<String> a = new ArrayList<>( data ); + final List<String> b = new ArrayList<>( data ); + + assertEquals(a, b); + assertTrue(ListUtils.isEqualList(a, b)); + a.clear(); + assertFalse(ListUtils.isEqualList(a, b)); + assertFalse(ListUtils.isEqualList(a, null)); + assertFalse(ListUtils.isEqualList(null, b)); + assertTrue(ListUtils.isEqualList(null, null)); + } + + @Test + public void testHashCode() { + final Collection<String> data = Arrays.asList("a", "b", "c"); + + final List<String> a = new ArrayList<>(data); + final List<String> b = new ArrayList<>(data); + + assertEquals(a.hashCode(), b.hashCode()); + assertEquals(a.hashCode(), ListUtils.hashCodeForList(a)); + assertEquals(b.hashCode(), ListUtils.hashCodeForList(b)); + assertEquals(ListUtils.hashCodeForList(a), ListUtils.hashCodeForList(b)); + a.clear(); + assertNotEquals(ListUtils.hashCodeForList(a), ListUtils.hashCodeForList(b)); + assertEquals(0, ListUtils.hashCodeForList(null)); + } + /** - * Tests intersecting a non-empty list with an empty list. + * Tests the {@code indexOf} method in {@code ListUtils} class.. */ @Test - public void testIntersectNonEmptyWithEmptyList() { - final List<String> empty = Collections.<String>emptyList(); - assertTrue(ListUtils.intersection(empty, fullList).isEmpty(), "result not empty"); + public void testIndexOf() { + Predicate<String> testPredicate = EqualPredicate.equalPredicate("d"); + int index = ListUtils.indexOf(fullList, testPredicate); + assertEquals(d, fullList.get(index)); + + testPredicate = EqualPredicate.equalPredicate("de"); + index = ListUtils.indexOf(fullList, testPredicate); + assertEquals(index, -1); + + assertEquals(ListUtils.indexOf(null, testPredicate), -1); + assertEquals(ListUtils.indexOf(fullList, null), -1); } /** @@ -71,19 +129,19 @@ public class ListUtilsTest { } /** - * Tests intersecting a non-empty list with an subset of itself. + * Tests intersecting two lists in different orders. */ @Test - public void testIntersectNonEmptySubset() { - // create a copy - final List<String> other = new ArrayList<>(fullList); - - // remove a few items - assertNotNull(other.remove(0)); - assertNotNull(other.remove(1)); - - // make sure the intersection is equal to the copy - assertEquals(other, ListUtils.intersection(fullList, other)); + public void testIntersectionOrderInsensitivity() { + final List<String> one = new ArrayList<>(); + final List<String> two = new ArrayList<>(); + one.add("a"); + one.add("b"); + two.add("a"); + two.add("a"); + two.add("b"); + two.add("b"); + assertEquals(ListUtils.intersection(one, two), ListUtils.intersection(two, one)); } /** @@ -105,38 +163,28 @@ public class ListUtilsTest { } /** - * Tests intersecting two lists in different orders. + * Tests intersecting a non-empty list with an subset of itself. */ @Test - public void testIntersectionOrderInsensitivity() { - final List<String> one = new ArrayList<>(); - final List<String> two = new ArrayList<>(); - one.add("a"); - one.add("b"); - two.add("a"); - two.add("a"); - two.add("b"); - two.add("b"); - assertEquals(ListUtils.intersection(one, two), ListUtils.intersection(two, one)); + public void testIntersectNonEmptySubset() { + // create a copy + final List<String> other = new ArrayList<>(fullList); + + // remove a few items + assertNotNull(other.remove(0)); + assertNotNull(other.remove(1)); + + // make sure the intersection is equal to the copy + assertEquals(other, ListUtils.intersection(fullList, other)); } + /** + * Tests intersecting a non-empty list with an empty list. + */ @Test - public void testPredicatedList() { - final Predicate<Object> predicate = o -> o instanceof String; - final List<Object> list = ListUtils.predicatedList(new ArrayList<>(), predicate); - assertTrue(list instanceof PredicatedList, "returned object should be a PredicatedList"); - try { - ListUtils.predicatedList(new ArrayList<>(), null); - fail("Expecting IllegalArgumentException for null predicate."); - } catch (final NullPointerException ex) { - // expected - } - try { - ListUtils.predicatedList(null, predicate); - fail("Expecting IllegalArgumentException for null list."); - } catch (final NullPointerException ex) { - // expected - } + public void testIntersectNonEmptyWithEmptyList() { + final List<String> empty = Collections.<String>emptyList(); + assertTrue(ListUtils.intersection(empty, fullList).isEmpty(), "result not empty"); } @Test @@ -181,158 +229,6 @@ public class ListUtilsTest { } @Test - public void testEmptyIfNull() { - assertTrue(ListUtils.emptyIfNull(null).isEmpty()); - - final List<Long> list = new ArrayList<>(); - assertSame(list, ListUtils.emptyIfNull(list)); - } - - @Test - public void testDefaultIfNull() { - assertTrue(ListUtils.defaultIfNull(null, Collections.emptyList()).isEmpty()); - - final List<Long> list = new ArrayList<>(); - assertSame(list, ListUtils.defaultIfNull(list, Collections.<Long>emptyList())); - } - - @Test - public void testEquals() { - final Collection<String> data = Arrays.asList("a", "b", "c"); - - final List<String> a = new ArrayList<>( data ); - final List<String> b = new ArrayList<>( data ); - - assertEquals(a, b); - assertTrue(ListUtils.isEqualList(a, b)); - a.clear(); - assertFalse(ListUtils.isEqualList(a, b)); - assertFalse(ListUtils.isEqualList(a, null)); - assertFalse(ListUtils.isEqualList(null, b)); - assertTrue(ListUtils.isEqualList(null, null)); - } - - @Test - public void testHashCode() { - final Collection<String> data = Arrays.asList("a", "b", "c"); - - final List<String> a = new ArrayList<>(data); - final List<String> b = new ArrayList<>(data); - - assertEquals(a.hashCode(), b.hashCode()); - assertEquals(a.hashCode(), ListUtils.hashCodeForList(a)); - assertEquals(b.hashCode(), ListUtils.hashCodeForList(b)); - assertEquals(ListUtils.hashCodeForList(a), ListUtils.hashCodeForList(b)); - a.clear(); - assertNotEquals(ListUtils.hashCodeForList(a), ListUtils.hashCodeForList(b)); - assertEquals(0, ListUtils.hashCodeForList(null)); - } - - @Test - public void testRetainAll() { - final List<String> sub = new ArrayList<>(); - sub.add(a); - sub.add(b); - sub.add(x); - - final List<String> retained = ListUtils.retainAll(fullList, sub); - assertEquals(2, retained.size()); - sub.remove(x); - assertEquals(retained, sub); - fullList.retainAll(sub); - assertEquals(retained, fullList); - - try { - ListUtils.retainAll(null, null); - fail("expecting NullPointerException"); - } catch(final NullPointerException npe){} // this is what we want - } - - @Test - public void testRemoveAll() { - final List<String> sub = new ArrayList<>(); - sub.add(a); - sub.add(b); - sub.add(x); - - final List<String> remainder = ListUtils.removeAll(fullList, sub); - assertEquals(3, remainder.size()); - fullList.removeAll(sub); - assertEquals(remainder, fullList); - - try { - ListUtils.removeAll(null, null); - fail("expecting NullPointerException"); - } catch(final NullPointerException npe) {} // this is what we want - } - - @Test - public void testSubtract() { - final List<String> list = new ArrayList<>(); - list.add(a); - list.add(b); - list.add(a); - list.add(x); - - final List<String> sub = new ArrayList<>(); - sub.add(a); - - final List<String> result = ListUtils.subtract(list, sub); - assertEquals(3, result.size()); - - final List<String> expected = new ArrayList<>(); - expected.add(b); - expected.add(a); - expected.add(x); - - assertEquals(expected, result); - - try { - ListUtils.subtract(list, null); - fail("expecting NullPointerException"); - } catch(final NullPointerException npe) {} // this is what we want - } - - @Test - public void testSubtractNullElement() { - final List<String> list = new ArrayList<>(); - list.add(a); - list.add(null); - list.add(null); - list.add(x); - - final List<String> sub = new ArrayList<>(); - sub.add(null); - - final List<String> result = ListUtils.subtract(list, sub); - assertEquals(3, result.size()); - - final List<String> expected = new ArrayList<>(); - expected.add(a); - expected.add(null); - expected.add(x); - - assertEquals(expected, result); - } - - /** - * Tests the {@code indexOf} method in {@code ListUtils} class.. - */ - @Test - public void testIndexOf() { - Predicate<String> testPredicate = EqualPredicate.equalPredicate("d"); - int index = ListUtils.indexOf(fullList, testPredicate); - assertEquals(d, fullList.get(index)); - - testPredicate = EqualPredicate.equalPredicate("de"); - index = ListUtils.indexOf(fullList, testPredicate); - assertEquals(index, -1); - - assertEquals(ListUtils.indexOf(null, testPredicate), -1); - assertEquals(ListUtils.indexOf(fullList, null), -1); - } - - @Test @SuppressWarnings("boxing") // OK in test code public void testLongestCommonSubsequence() { @@ -447,7 +343,62 @@ public class ListUtilsTest { assertEquals(strings, partitionMax.get(0)); } - private static final Predicate<Number> EQUALS_TWO = input -> input.intValue() == 2; + @Test + public void testPredicatedList() { + final Predicate<Object> predicate = o -> o instanceof String; + final List<Object> list = ListUtils.predicatedList(new ArrayList<>(), predicate); + assertTrue(list instanceof PredicatedList, "returned object should be a PredicatedList"); + try { + ListUtils.predicatedList(new ArrayList<>(), null); + fail("Expecting IllegalArgumentException for null predicate."); + } catch (final NullPointerException ex) { + // expected + } + try { + ListUtils.predicatedList(null, predicate); + fail("Expecting IllegalArgumentException for null list."); + } catch (final NullPointerException ex) { + // expected + } + } + + @Test + public void testRemoveAll() { + final List<String> sub = new ArrayList<>(); + sub.add(a); + sub.add(b); + sub.add(x); + + final List<String> remainder = ListUtils.removeAll(fullList, sub); + assertEquals(3, remainder.size()); + fullList.removeAll(sub); + assertEquals(remainder, fullList); + + try { + ListUtils.removeAll(null, null); + fail("expecting NullPointerException"); + } catch(final NullPointerException npe) {} // this is what we want + } + + @Test + public void testRetainAll() { + final List<String> sub = new ArrayList<>(); + sub.add(a); + sub.add(b); + sub.add(x); + + final List<String> retained = ListUtils.retainAll(fullList, sub); + assertEquals(2, retained.size()); + sub.remove(x); + assertEquals(retained, sub); + fullList.retainAll(sub); + assertEquals(retained, fullList); + + try { + ListUtils.retainAll(null, null); + fail("expecting NullPointerException"); + } catch(final NullPointerException npe){} // this is what we want + } @Test @SuppressWarnings("boxing") // OK in test code @@ -486,4 +437,53 @@ public class ListUtilsTest { assertTrue(output1.contains(3L)); assertTrue(output1.contains(4L)); } + + @Test + public void testSubtract() { + final List<String> list = new ArrayList<>(); + list.add(a); + list.add(b); + list.add(a); + list.add(x); + + final List<String> sub = new ArrayList<>(); + sub.add(a); + + final List<String> result = ListUtils.subtract(list, sub); + assertEquals(3, result.size()); + + final List<String> expected = new ArrayList<>(); + expected.add(b); + expected.add(a); + expected.add(x); + + assertEquals(expected, result); + + try { + ListUtils.subtract(list, null); + fail("expecting NullPointerException"); + } catch(final NullPointerException npe) {} // this is what we want + } + + @Test + public void testSubtractNullElement() { + final List<String> list = new ArrayList<>(); + list.add(a); + list.add(null); + list.add(null); + list.add(x); + + final List<String> sub = new ArrayList<>(); + sub.add(null); + + final List<String> result = ListUtils.subtract(list, sub); + assertEquals(3, result.size()); + + final List<String> expected = new ArrayList<>(); + expected.add(a); + expected.add(null); + expected.add(x); + + assertEquals(expected, result); + } }