jef.tools.collection.CollectionUtils.java Source code

Java tutorial

Introduction

Here is the source code for jef.tools.collection.CollectionUtils.java

Source

/*
 * 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);
        }
    }
}