/* * #! * Ontopia Engine * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * 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 net.ontopia.utils; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Random; import java.util.Set; /** * INTERNAL: Class that contains useful collection methods. */ public class CollectionUtils { private static final Random random = new Random(); private CollectionUtils() { } /** * INTERNAL: Gets the first object in the collection. If the * collection is empty, null is returned. */ public static <T> T getFirst(Collection<T> coll) { if (coll == null || coll.isEmpty()) return null; // If it's a list return it directly if (coll instanceof List) return ((List<T>)coll).get(0); Iterator<T> iter = coll.iterator(); while (iter.hasNext()) { return iter.next(); } return null; } /** * INTERNAL: Gets the first object in the collection. If the * collection does not contain any elements NoSuchElementException * is thrown.<p> * * @since 1.3.4 */ public static <T> T getFirstElement(Collection<T> coll) throws NoSuchElementException { if (coll instanceof List) try { return ((List<T>)coll).get(0); } catch (IndexOutOfBoundsException e) { throw new NoSuchElementException(); } else return coll.iterator().next(); } public static <T> Collection<T> getSingletonCollectionOrEmptyIfNull(T o) { if (o == null) return Collections.emptySet(); else return Collections.singleton(o); } /** * INTERNAL: Gets a random object from the collection. If the * collection is empty, null is returned. */ public static <T> T getRandom(Collection<T> coll) { if (coll == null || coll.isEmpty()) return null; int chosen = random.nextInt(coll.size()); // NOTE: Test should ideally be against java.util.RandomAccess, // but it's only supported with JDK 1.4 // If it's a list return it directly if (coll instanceof List) return ((List<T>)coll).get(chosen); // Otherwise loop through the collection long count = 0; Iterator<T> iter = coll.iterator(); while (iter.hasNext()) { T obj = iter.next(); if (count == chosen) return obj; count++; } return null; } /** * INTERNAL: Compares two collections to see if they contain the same * elements. * * @since 1.4.1 */ public static <T> boolean equalsUnorderedSet(Collection<T> coll1, Collection<T> coll2) { // Take care of nulls if (coll1 == null) if (coll2 == null) // 1: null 2: null return true; else // 1: null 2: not null return false; else if (coll2 == null) // 1: not null 2: null return false; // Compare set size int size1 = coll1.size(); int size2 = coll2.size(); if (size1 != size2) return false; // If both have 1 element compare first element if (size1 == 1) { T obj1 = coll1.iterator().next(); T obj2 = coll2.iterator().next(); return (obj1 == null ? obj2 == null : obj1.equals(obj2)); } // Compare collections as sets if (coll1 instanceof Set) if (coll2 instanceof Set) return coll1.equals(coll2); else return coll1.equals(new HashSet<T>(coll2)); else if (coll2 instanceof Set) return coll2.equals(new HashSet<T>(coll1)); else return new HashSet<T>(coll2).equals(new HashSet<T>(coll1)); } /** * EXPERIMENTAL: Iterates over up to <i>length</i> number of * elements in the iterator and returns those elements as a * Collection. If the iterator is exhausted only the iterated * elements are returned. */ public static <T> List<T> nextBatch(Iterator<T> iter, int length) { List<T> batch = new ArrayList<T>(length); int i = 0; do { batch.add(iter.next()); i++; } while (i < length && iter.hasNext()); return batch; } /** * EXPERIMENTAL: Iterates over up to <i>length</i> number of * elements in the iterator and adds those elements to the given * collection. If the iterator is exhausted only the iterated * elements are added. * * @return the number of elements inserted into the array */ public static <T> int nextBatch(Iterator<T> iter, int length, Collection<T> batch) { int i = 0; do { batch.add(iter.next()); i++; } while (i < length && iter.hasNext()); return i; } /** * EXPERIMENTAL: Iterates over up to <i>values.length</i> number of * elements in the iterator and inserts those elements in the * <i>values</i> array. If the iterator is exhausted only the iterated * elements are included. * * @return the number of elements inserted into the array */ public static <T> int nextBatch(Iterator<T> iter, T[] values) { int i = 0; do { values[i] = iter.next(); i++; } while (i < values.length && iter.hasNext()); return i; } /** * EXPERIMENTAL: Iterates over up to <i>length</i> number of * elements in the iterator and inserts those elements in the * <i>values</i> array. The first element is inserted at the specified * <i>offset</i>. If the iterator is exhausted only the iterated * elements are included. * * @return the number of elements inserted into the array */ public static <T> int nextBatch(Iterator<T> iter, T[] values, int offset, int length) { int i = 0; do { values[offset+i] = iter.next(); i++; } while (i < length && iter.hasNext()); return offset + i; } /** * INTERNAL: Cast collection as list or make a new list. */ public static <T> List<T> castList(Collection<T> c) { if (c instanceof List) return (List<T>)c; else return new ArrayList<T>(c); } /** * INTERNAL: Adds all elements in the array to the collection. */ public static <T> void addAll(Collection<T> c, T[] a) { for (int i=0; i < a.length; i++) { c.add(a[i]); } } /** * INTERNAL: Returns true if the two collections overlap with one or * more elements. */ public static <T> boolean overlaps(Collection<T> c1, Collection<T> c2) { if (c1.size() > c2.size()) return _overlaps(c2, c1); else return _overlaps(c1, c2); } private static <T> boolean _overlaps(Collection<T> c1, Collection<T> c2) { // NOTE: loop over smallest collection, which should always be the first argument Iterator<T> iter = c1.iterator(); while (iter.hasNext()) { if (c2.contains(iter.next())) return true; } return false; } /** * INTERNAL: Creates new concurrent java.util.Map instance. */ public static <K, V> Map<K, V> createConcurrentMap() { try { Class<?> klass = Class.forName("java.util.concurrent.ConcurrentHashMap"); return (Map<K, V>)klass.newInstance(); } catch (Exception e1) { try { Class<?> klass = Class.forName("EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap"); return (Map<K, V>)klass.newInstance(); } catch (Exception e2) { return Collections.synchronizedMap(new HashMap<K, V>()); } } } /** * INTERNAL: Creates new concurrent java.util.Set instance. */ public static <T> Set<T> createConcurrentSet() { try { Class<?> klass = Class.forName("EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArraySet"); return (Set<T>)klass.newInstance(); } catch (Exception e1) { return Collections.synchronizedSet(new HashSet<T>()); } } /** * INTERNAL: Creates new Set that contains the elements from the * input collection that the decider deems ok. */ public static <T> Set<T> filterSet(Collection<T> coll, DeciderIF decider) { if (coll.isEmpty()) return Collections.emptySet(); Set<T> result = new HashSet<T>(coll.size()); Iterator<T> iter = coll.iterator(); while (iter.hasNext()) { T o = iter.next(); if (decider.ok(o)) result.add(o); } return result; } /** * INTERNAL: Removes all except the first occurrence of each element * in the list. Use only with fairly small RandomAccess * collections. Method trades speed for memory. */ public static <T> List<T> removeDuplicates(List<T> list) { int size = list.size(); for (int index=0; index < size; index++) { // remove all but first occurrence T elem = list.get(index); for (int i=index+1; i < size; i++) { if (ObjectUtils.equals(elem, list.get(i))) { list.remove(i); size--; } } } return list; } }