Java tutorial
/* * JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com) * * 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 jef.tools.collection; import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.AbstractList; import java.util.AbstractMap; import java.util.AbstractQueue; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Queue; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.apache.commons.lang.ObjectUtils; import com.google.common.base.Function; import com.google.common.base.Objects; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Collections2; import com.google.common.collect.Multimap; import jef.common.wrapper.ArrayIterator; import jef.tools.ArrayUtils; import jef.tools.Assert; import jef.tools.reflect.ClassEx; import jef.tools.reflect.FieldEx; import jef.tools.reflect.GenericUtils; /** * ?? v2.0 * * @author Joe */ public class CollectionUtils { /** * ?SortedMap */ @SuppressWarnings({ "rawtypes", "unchecked" }) private static final SortedMap EMPTY_SORTEDMAP = Collections.unmodifiableSortedMap(new TreeMap()); private CollectionUtils() { } public static final class C extends CollectionUtils { } /** * ? * * @param source * @param function * @return */ public static <K, V> List<V> convert(List<K> source, Function<K, V> function) { List<V> result = new ArrayList<V>(source.size()); for (int i = 0; i < source.size(); i++) { result.add(function.apply(source.get(i))); } return result; } /** * ?MapMap???? * * @param array * * @param keyExtractor * ??? * @return ??????Map?key? * {@linkplain #group(Collection, Function)} */ public static <K, V> Map<K, V> group(V[] array, Function<V, K> keyExtractor) { if (array == null || array.length == 0) return Collections.emptyMap(); Map<K, V> result = new HashMap<K, V>(array.length); for (V value : array) { K key = keyExtractor.apply(value); result.put(key, value); } return result; } /** * ?MapMapKey? * * @param array * * @param keyExtractor * ??? * @param comp * * @return ??????Map?key? * {@linkplain #group(Collection, Function)} */ @SuppressWarnings("unchecked") public static <K, V> SortedMap<K, V> groupToSortedMap(V[] array, Function<V, K> keyExtractor, Comparator<K> comp) { if (array == null || array.length == 0) return EMPTY_SORTEDMAP; SortedMap<K, V> result = new TreeMap<K, V>(comp); for (V value : array) { K key = keyExtractor.apply(value); result.put(key, value); } return result; } /** * ??MapMapKey? * * @param collection * ? * @param keyExtractor * ??? * @param comp * * @return ??????Map?key? * {@linkplain #group(Collection, Function)} */ @SuppressWarnings("unchecked") public static <K, V> SortedMap<K, V> groupToSortedMap(Collection<V> collection, Function<V, K> keyExtractor, Comparator<K> comp) { if (collection == null || collection.size() == 0) return EMPTY_SORTEDMAP; SortedMap<K, V> result = new TreeMap<K, V>(comp); for (V value : collection) { K key = keyExtractor.apply(value); result.put(key, value); } return result; } /** * ?????? * * @param array * * @param extractor * ??? * @param ignoreNull * true???null * @return ????? */ public static <T, A> List<T> extract(A[] array, Function<A, T> extractor) { return extract(array, extractor, false); } /** * ?????? * * @param array * * @param extractor * ??? * @param ignoreNull * true???null * @return ????? */ public static <T, A> List<T> extract(A[] array, Function<A, T> extractor, boolean ignoreNull) { List<T> result = new ArrayList<T>(array.length); if (array != null) { for (A a : array) { T t = extractor.apply(a); if (ignoreNull && t == null) { continue; } result.add(t); } } return result; } /** * ??????? * * @param collection * ? * @param extractor * ??? * @return ????? */ public static <T, A> List<T> extract(Collection<A> collection, Function<A, T> extractor) { return extract(collection, extractor, false); } /** * ??????? * * @param collection * ? * @param extractor * ??? * @param ignoreNull * true???null * @return ????? */ public static <T, A> List<T> extract(Collection<A> collection, Function<A, T> extractor, boolean ignoreNull) { List<T> result = new ArrayList<T>(collection.size()); if (collection != null) { for (A a : collection) { T t = extractor.apply(a); if (ignoreNull && t == null) { continue; } result.add(t); } } return result; } /** * ?{@link #extract(Collection, Function)} * extract?transform * * @param collection * ? * @param extractor * ??? * @return ?????? */ public static <T, A> Collection<T> transform(Collection<A> collection, Function<A, T> extractor) { return Collections2.transform(collection, extractor); } /** * ?{@link #extract(Collection, Function)} * extract?transform * * @param array * * @param extractor * ??? * @return ?????? */ public static <T, A> Collection<T> transform(A[] array, Function<A, T> extractor) { return Collections2.transform(Arrays.asList(array), extractor); } /** * Map?Key?Value,Value?key * value?????{@link Multimap} * * ?Map * * <pre> * <tt>{tom: 100},{jack: 95},{king: 88}, {mar: 77}, {jim: 88}</tt> * </pre> * * ?map * * <pre> * <tt>{100:[tom]}, {95:[jack]}, {88: [king,jim]}, {77:[mar]}</tt> * </pre> * * @param <K> * @param <V> * @param map * ???Map * @return A new Multimap that reverse key and value */ public static <K, V> Multimap<V, K> inverse(Map<K, V> map) { Multimap<V, K> result = ArrayListMultimap.create(); for (Entry<K, V> e : map.entrySet()) { result.put(e.getValue(), e.getKey()); } return result; } /** * ? * * @param collection * ?? * @param function * ?Key * @return ???Key?Value */ public static <T, A> Multimap<A, T> group(Collection<T> collection, Function<T, A> function) { Assert.notNull(collection); Multimap<A, T> result = ArrayListMultimap.create(); for (T value : collection) { A attrib = function.apply(value); result.put(attrib, value); } return result; } /** * Collection?MapMap???? * * @param collection * ? * @param keyExtractor * ??? * @param checkIsDup true??value?equalequals?replace? * false??equalvalue?value * * @return ??????Map????? */ public static <T, V> Map<T, V> groupWithReplace(Collection<V> collection, Function<V, T> keyExtractor, boolean checkIsDup) { if (collection == null || collection.isEmpty()) return Collections.emptyMap(); Map<T, V> result = new HashMap<T, V>(collection.size()); for (V value : collection) { T key = keyExtractor.apply(value); V oldValue = result.put(key, value); if (oldValue != null && checkIsDup) { if (!oldValue.equals(value)) { throw new IllegalStateException( "Detect two different objects with a same key, and one object will be replaced in the map." + value + " to " + oldValue); } } } return result; } /** * ??? * * @param collection * ? * @param filter * * @return */ public static <T> T findFirst(Collection<T> collection, Function<T, Boolean> filter) { if (collection == null || collection.isEmpty()) return null; for (T obj : collection) { if (filter.apply(obj)) { return obj; } } return null; } /** * ??? * * @param collection * ? * @param fieldname * ?? * @param value * * @return */ public static <T> T findFirst(Collection<T> collection, String fieldname, Object value) { if (collection == null || collection.isEmpty()) return null; Class<?> clz = collection.iterator().next().getClass(); FieldValueFilter<T> f = new FieldValueFilter<T>(clz, fieldname, value); return findFirst(collection, f); } /** * ??? * * @param <T> * * @param collection * ? * @param fieldname * * @param value * * @return ?? */ public static <T> List<T> getFiltered(Collection<T> collection, String fieldname, Object value) { if (collection.isEmpty()) return Collections.emptyList(); Class<?> clz = collection.iterator().next().getClass(); return getFiltered(collection, new FieldValueFilter<T>(clz, fieldname, value)); } /** * ??? * * @param <T> * * @param collection * ? * @param filter * * @return */ public static <T> List<T> getFiltered(Collection<T> collection, Function<T, Boolean> filter) { List<T> list = new ArrayList<T>(); if (collection == null || collection.isEmpty()) return list; for (T obj : collection) { if (filter.apply(obj)) { list.add(obj); } } return list; } /** * ??? * * @param <T> * * @param collection * ? * @param filter * * @return */ public static <T> void filter(Collection<T> collection, Function<T, Boolean> filter) { for (Iterator<T> iter = collection.iterator(); iter.hasNext();) { T e = iter.next(); if (!Boolean.TRUE.equals(filter.apply(e))) { iter.remove(); } } } /** * MapMap. ?MapMap??? * * @param map * ??Map * @param filter * * @return ?Map */ public static <K, V> Map<K, V> getFiltered(Map<K, V> map, Function<Map.Entry<K, V>, Boolean> filter) { Map<K, V> result; if (map instanceof SortedMap) { result = new TreeMap<K, V>(((SortedMap<K, V>) map).comparator()); } else { result = new HashMap<K, V>(map.size()); } for (Map.Entry<K, V> e : map.entrySet()) { Boolean b = filter.apply(e); if (Boolean.TRUE.equals(b)) { result.put(e.getKey(), e.getValue()); } } return result; } /** * Mapfiltertrue???<br> * ?Map??Iterator.remove()?( * UnsupportedOperationException) * * @param map * ??Map * @param filter * Function?? * @throws UnsupportedOperationException */ public static <K, V> void filter(Map<K, V> map, Function<Map.Entry<K, V>, Boolean> filter) { for (Iterator<Map.Entry<K, V>> iter = map.entrySet().iterator(); iter.hasNext();) { Map.Entry<K, V> e = iter.next(); if (!Boolean.TRUE.equals(filter.apply(e))) { iter.remove(); } } } /** * Enumation?Collection * * @param data * @param type * @return */ @SuppressWarnings("unchecked") public static <T> Collection<T> toCollection(Object data, Class<T> type) { if (data == null) return null; if (data instanceof Collection) { return ((Collection<T>) data); } else if (data.getClass().isArray()) { if (data.getClass().getComponentType().isPrimitive()) { int len = Array.getLength(data); List<T> result = new ArrayList<T>(len); for (int i = 0; i < len; i++) { result.add((T) Array.get(data, i)); } } else { return Arrays.asList((T[]) data); } } else if (data instanceof Enumeration) { Enumeration<T> e = (Enumeration<T>) data; List<T> result = new ArrayList<T>(); for (; e.hasMoreElements();) { result.add(e.nextElement()); } return result; } throw new IllegalArgumentException("The input type " + data.getClass() + " can not convert to Collection."); } /** * ????? * * @param data * ? * @param clz * @return */ @SuppressWarnings("unchecked") public static <T> Iterator<T> iterator(Object data, Class<T> clz) { if (data == null) return null; if (data instanceof Collection) { return ((Collection<T>) data).iterator(); } else if (data.getClass().isArray()) { return new ArrayIterator<T>(data); } else if (data instanceof Enumeration) { return new EnumerationIterator<T>((Enumeration<T>) data); } return null; } /** * Enumeration?List * * @param data * @return ??List */ public static <E> List<E> toList(Enumeration<E> data) { List<E> result = new ArrayList<E>(); for (; data.hasMoreElements();) { result.add(data.nextElement()); } return result; } /** * Iterable?List * * @param data * @return ??List */ public static <E> List<E> toList(Iterable<E> data) { List<E> result = new ArrayList<E>(); for (Iterator<E> iter = data.iterator(); iter.hasNext();) { result.add(iter.next()); } return result; } /** * ???? * * @param data * @return */ public static <E> Iterator<E> iterator(Enumeration<E> data) { return new EnumerationIterator<E>(data); } /** * ?? * * @param type * @return true if type is a collection type. */ public static boolean isArrayOrCollection(Type type) { if (type instanceof GenericArrayType) { return true; } else if (type instanceof Class) { Class<?> rawType = (Class<?>) type; return rawType.isArray() || Collection.class.isAssignableFrom(rawType); } else { return Collection.class.isAssignableFrom(GenericUtils.getRawClass(type)); } } /** * ?Collection * * @param type * @return true if type is a collection type. */ public static boolean isCollection(Type type) { if (type instanceof GenericArrayType) { return false; } else if (type instanceof Class) { Class<?> rawType = (Class<?>) type; return Collection.class.isAssignableFrom(rawType); } else { return Collection.class.isAssignableFrom(GenericUtils.getRawClass(type)); } } /** * ? * * @param type * @return ??null,??? */ public static Type getComponentType(Type type) { if (type instanceof GenericArrayType) { return ((GenericArrayType) type).getGenericComponentType(); } else if (type instanceof Class) { Class<?> rawType = (Class<?>) type; if (rawType.isArray()) { return rawType.getComponentType(); } else if (Collection.class.isAssignableFrom(rawType)) { // ??Object return Object.class; } } else if (type instanceof ParameterizedType) { ParameterizedType pType = (ParameterizedType) type; Type rawType = pType.getRawType(); if (isCollection(rawType)) { return pType.getActualTypeArguments()[0]; } } return null; } /** * ? ?class * * @param type * @return */ public static Class<?> getSimpleComponentType(Type type) { Type result = getComponentType(type); if (result instanceof Class<?>) { return (Class<?>) result; } // ??/???Class????? return null; } /** * ? * * @param obj * @return */ @SuppressWarnings("rawtypes") public static int length(Object obj) { if (obj.getClass().isArray()) { return Array.getLength(obj); } Assert.isTrue(obj instanceof Collection); return ((Collection) obj).size(); } /** * ???? */ @SuppressWarnings("rawtypes") public static Object createContainerInstance(ClassEx collectionType, int size) { Class raw = collectionType.getWrappered(); try { if (collectionType.isArray()) { if (size < 0) size = 0; Object array = Array.newInstance(GenericUtils.getRawClass(collectionType.getComponentType()), size); return array; } else if (!Modifier.isAbstract(collectionType.getModifiers())) {// ?? Object c = raw.newInstance(); return c; } else if (Object.class == raw || raw == List.class || raw == AbstractList.class) { return new ArrayList(); } else if (raw == Set.class || raw == AbstractSet.class) { return new HashSet(); } else if (raw == Map.class || raw == AbstractMap.class) { return new HashMap(); } else if (raw == Queue.class || raw == AbstractQueue.class) { return new LinkedList(); } else { throw new IllegalArgumentException( "Unknown collection class for create:" + collectionType.getName()); } } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } /** * ???? * * @param <T> * @param a * ?A * @param b * ?B * @return */ public static <T> Collection<T> union(Collection<T> a, Collection<T> b) { HashSet<T> s = new HashSet<T>(a.size() + b.size()); s.addAll(a); s.addAll(b); return s; } /** * Return <code>true</code> if the supplied Collection is <code>null</code> * or empty. Otherwise, return <code>false</code>. * * @param collection * the Collection to check * @return whether the given Collection is empty */ public static boolean isEmpty(Collection<?> collection) { return (collection == null || collection.isEmpty()); } /** * Return <code>true</code> if the supplied Map is <code>null</code> or * empty. Otherwise, return <code>false</code>. * * @param map * the Map to check * @return whether the given Map is empty */ public static boolean isEmpty(Map<?, ?> map) { return (map == null || map.isEmpty()); } /** * Convert the supplied array into a List. A primitive array gets converted * into a List of the appropriate wrapper type. * <p> * A <code>null</code> source value will be converted to an empty List. * * @param source * the (potentially primitive) array * @return the converted List result * @see ObjectUtils#toObjectArray(Object) */ public static List<?> arrayToList(Object source) { return Arrays.asList(ArrayUtils.toObject(source)); } /** * Check whether the given Iterator contains the given element. * * @param iterator * the Iterator to check * @param element * the element to look for * @return <code>true</code> if found, <code>false</code> else */ public static boolean contains(Iterable<?> iterable, Object element) { if (iterable != null) { Iterator<?> iterator = iterable.iterator(); while (iterator.hasNext()) { Object candidate = iterator.next(); if (Objects.equal(candidate, element)) { return true; } } } return false; } /** * Check whether the given Enumeration contains the given element. * * @param enumeration * the Enumeration to check * @param element * the element to look for * @return <code>true</code> if found, <code>false</code> else */ public static boolean contains(Enumeration<?> enumeration, Object element) { if (enumeration != null) { while (enumeration.hasMoreElements()) { Object candidate = enumeration.nextElement(); if (Objects.equal(candidate, element)) { return true; } } } return false; } /** * Check whether the given Collection contains the given element instance. * <p> * Enforces the given instance to be present, rather than returning * <code>true</code> for an equal element as well. * * @param collection * the Collection to check * @param element * the element to look for * @return <code>true</code> if found, <code>false</code> else */ public static boolean fastContains(Collection<?> collection, Object element) { if (collection != null) { for (Object candidate : collection) { if (candidate == element) { return true; } } } return false; } /** * Return <code>true</code> if any element in '<code>candidates</code>' is * contained in '<code>source</code>'; otherwise returns <code>false</code>. * * @param source * the source Collection * @param candidates * the candidates to search for * @return whether any of the candidates has been found */ public static boolean containsAny(Collection<?> source, Collection<?> candidates) { if (isEmpty(source) || isEmpty(candidates)) { return false; } for (Object candidate : candidates) { if (source.contains(candidate)) { return true; } } return false; } /** * Find the common element type of the given Collection, if any.<br> * ??????null * * @param collection * the Collection to check * @return the common element type, or <code>null</code> if no clear common * type has been found (or the collection was empty) * */ public static Class<?> findCommonElementType(Collection<?> collection) { if (isEmpty(collection)) { return null; } Class<?> candidate = null; for (Object val : collection) { if (val != null) { if (candidate == null) { candidate = val.getClass(); } else if (candidate != val.getClass()) { return null; } } } return candidate; } /** * * Create a new identityHashSet. * * @return */ public static <E> Set<E> identityHashSet() { return Collections.newSetFromMap(new IdentityHashMap<E, Boolean>()); } /** * Iterator wrapping an Enumeration. */ private static class EnumerationIterator<E> implements Iterator<E> { private Enumeration<E> enumeration; public EnumerationIterator(Enumeration<E> enumeration) { this.enumeration = enumeration; } public boolean hasNext() { return this.enumeration.hasMoreElements(); } public E next() { return this.enumeration.nextElement(); } public void remove() throws UnsupportedOperationException { throw new UnsupportedOperationException("Not supported"); } } /** * ?? * * @author Administrator * @Date 2011-6-15 * @param <T> */ private static class FieldValueFilter<T> implements Function<T, Boolean> { private FieldEx field; private Object value; public FieldValueFilter(Class<?> clz, String fieldname, Object value) { ClassEx cw = new ClassEx(clz); this.field = cw.getField(fieldname); Assert.notNull(this.field, "the field " + fieldname + " is not found in class " + cw.getName()); this.value = value; } public Boolean apply(T input) { try { Object v = field.get(input); return Objects.equal(v, value); } catch (IllegalArgumentException e) { throw new IllegalAccessError(e.getMessage()); } } } /** * ?? * * @param collection * @return */ public static <T> T last(List<T> collection) { if (collection == null || collection.isEmpty()) { return null; } return collection.get(collection.size() - 1); } /** * List???list * * @param list * List * @param index * ?? * @param value * */ public static <T> void setElement(List<T> list, int index, T value) { if (index == list.size()) { list.add(value); } else if (index > list.size()) { for (int i = list.size(); i < index; i++) { list.add(null); } list.add(value); } else { list.set(index, value); } } }