/******************************************************************************* * Copyright 2014 Felipe Takiyama * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package br.usp.poli.takiyama.utils; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import br.usp.poli.takiyama.prv.Replaceable; import br.usp.poli.takiyama.prv.Substitution; /** * This class provides set operations for lists. * * @author Felipe Takiyama * */ public final class Lists { private Lists() { // avoids instantiation } /** * Returns the list resulting from the union of the specified lists. * <p> * The order of elements in the result is determined by the order of * elements in <code>list1</code>. If the element is not in this list, * then its ordering is determined by the order of elements in * <code>list2</code>. * </p> * @param <T> The type of element contained in the list * @param list1 The first list to add to the union * @param list2 The second list to add to the union * @return The list resulting from the union of the specified lists */ public static final <T> List<T> union(List<T> list1, List<T> list2) { Set<T> set = new LinkedHashSet<T>(list1.size() + list2.size()); set.addAll(list1); set.addAll(list2); return new ArrayList<T>(set); } /** * Returns the result of removing the elements of <code>list2</code> from * <code>list1</code>. The order of elements in <code>list1</code> is * preserved. * * @param <T> The type of element contained in the list * @param list1 The minuend * @param list2 The subtrahend * @return The list resulting from subtracting the second list from the * first list. */ public static final <T> List<T> difference(List<T> list1, List<T> list2) { Set<T> set = new LinkedHashSet<T>(list1.size()); set.addAll(list1); set.removeAll(list2); return new ArrayList<T>(set); } public static final <T> List<T> intersection(List<T> list1, List<T> list2) { List<T> list = new ArrayList<T>(); for (T t : list1) { if (list2.contains(t)) { list.add(t); } } return list; } /** * Returns a list containing one element. * @param <T> The type of element contained in the list * @param e1 The element to put in the list * @return a list containing the specified elements. */ public static final <T> List<T> listOf(T e1) { List<T> list = new ArrayList<T>(1); list.add(e1); return list; } /** * Returns a list containing the specified elements. * @param <T> The type of element contained in the list * @param e1 The first element of the list * @param e2 The second element of the list * @return a list containing the specified elements. */ public static final <T> List<T> listOf(T e1, T e2) { List<T> list = new ArrayList<T>(2); list.add(e1); list.add(e2); return list; } /** * Returns a list containing the specified elements. * @param <T> The type of element contained in the list * @param e1 The first element of the list * @param e2 The second element of the list * @param e3 The second element of the list * @return a list containing the specified elements. */ public static final <T> List<T> listOf(T e1, T e2, T e3) { List<T> list = new ArrayList<T>(3); list.add(e1); list.add(e2); list.add(e3); return list; } public static final <T> List<T> listOf(T ... elements) { return Arrays.asList(elements); } /** * Returns a lists with size <code>n</code> filled with the specified * element. * @param <T> The type of element contained in the list * @param e The element to fill the list * @param n The size of the list * @return a lists with size <code>n</code> filled with the specified * element. */ public static final <T> List<T> listOf(T e, int n) { List<T> list = new ArrayList<T>(n); for (int i = 0; i < n; i++) { list.add(e); } return list; } /** * Returns a list containing the elements of the specified * {@link Collection}. The order of the elements is determined * by the collections iterator. * * @param <T> The type of element contained in the list * @param c The collection to from where the elements will be gathered * @return a list containing the elements of the specified collection */ public static final <T> List<T> listOf(Collection<? extends T> c) { List<T> list = new ArrayList<T>(c.size()); list.addAll(c); return list; } /** * TODO Make it more generic * Puts the specified element the specified number of times in the list. * * @param <T> The type of element to put in the list * @param list The container where the elements will be put * @param element The element to fill the list * @param num The number of elements to put in the list * @throws IllegalArgumentException If <code>num</code> < 0. */ public static final <T> void fill(List<? super T> list, T element, int num) throws IllegalArgumentException { if (num < 0) { throw new IllegalArgumentException(); } for (int i = 0; i < num; i++) { list.add(element); } } // /** // * Assigns the specified Object reference to each element of the specified // * range of the specified list The range to be filled // * extends from index <tt>fromIndex</tt>, inclusive, to index // * <tt>toIndex</tt>, exclusive. (If <tt>fromIndex==toIndex</tt>, the // * range to be filled is empty.) // * // * @param <T> The type of element to put in the list // * @param list the list to be filled // * @param fromIndex the index of the first element (inclusive) to be // * filled with the specified value // * @param toIndex the index of the last element (exclusive) to be // * filled with the specified value // * @param val the value to be stored in all elements of the array // * @throws IllegalArgumentException if <tt>fromIndex > toIndex</tt> // * @throws ArrayIndexOutOfBoundsException if <tt>fromIndex < 0</tt> // */ // public static final <T> void fill(List<? super T> list, int fromIndex, int toIndex, T val) { // rangeCheck(fromIndex, toIndex); // for (int i = fromIndex; i < toIndex; i++) { // list.set(i, val); // } // } // // // /** // * Check that fromIndex and toIndex are in range, and throw an // * appropriate exception if they aren't. // */ // private static void rangeCheck(int fromIndex, int toIndex) { // if (fromIndex > toIndex) // throw new IllegalArgumentException("fromIndex(" + fromIndex + // ") > toIndex(" + toIndex+")"); // if (fromIndex < 0) // throw new ArrayIndexOutOfBoundsException(fromIndex); // } /** * Returns <code>true</code> if the specified lists of {@link BigDecimal} * are equal. Equality is verified using * {@link BigDecimal#compareTo(BigDecimal)}, which does not take into * account the scale of numbers. Thus, {2.0} and {2.00} are considered * to be the same list. * * @param arg1 The first list to compare * @param arg2 The second list to compare * @return <code>true</code> if the specified lists of {@link BigDecimal} * are equal * @see BigDecimal#compareTo(BigDecimal) */ public static final boolean areEqual(List<BigDecimal> arg1, List<BigDecimal> arg2) { boolean areEqual = true; if (arg1.size() == arg2.size()) { for (int i = 0; i < arg1.size(); i++) { if (arg1.get(i).compareTo(arg2.get(i)) != 0) { areEqual = false; break; } } } else { areEqual = false; } return areEqual; } /** * Returns the hash code for the specified list of {@link BigDecimal}. * <p> * This method should be used in association with * {@link #areEqual(List, List)} to maintain consistency between * hashCode() and equals(). * </p> * @param list A list of {@link BigDecimal} * @return The hash code for the specified list */ public static final int hashCode(List<BigDecimal> list) { int result = 1; for (BigDecimal element : list) { // set scale to 15, but this is an arbitrary number //result = 31 * result + (element == null ? 0 : element.setScale(15, RoundingMode.HALF_EVEN).hashCode()); // Put a math context in every operation, so i should not need to set scale here result = 31 * result + (element == null ? 0 : element.hashCode()); } return result; } /** * Returns the result of applying the specified substitution to the * elements of the specified list. * <p> * When the result of applying of the substitution to a given element from * the list is invalid, it is not added to the returned list. * </p> * * @param <T> A {@link Replaceable} type * @param s The substitution to be made * @param list The list containing the elements to be substituted * @return The result of applying the specified substitution to the * elements of the specified list. */ public static final <T extends Replaceable<T>> List<T> apply(Substitution s, List<T> list) { List<T> replaced = new ArrayList<T>(list.size()); for (Replaceable<T> element : list) { try { replaced.add(element.apply(s)); } catch (IllegalArgumentException e) { // invalid replaceable is not added to the list } catch (IllegalStateException e) { // invalid replaceable is not added to the set } } return replaced; } /** * Replaces the specified element with another element of same type in the * specified list. * @param <T> * @param list * @param toReplace * @param replacement * @return */ public static final <T> List<T> replace(List<T> list, T toReplace, T replacement) { List<T> replaced = new ArrayList<T>(list); int index = list.indexOf(toReplace); if (index != -1) { replaced.set(index, replacement); } return replaced; } /** * Returns <code>true</code> if the specified lists have the same elements. * @param <T> * @param list1 * @param list2 * @return */ public static final <T> boolean sameElements(List<T> list1, List<T> list2) { if (list1.size() != list2.size()) { return false; } return (list1.containsAll(list2) && list2.containsAll(list1)); } }