Collections

使い勝手や使い道は聞かないで。
面白そうだから作ってみただけ。

public interface Executer<V> {

    public V execute(int index, Object... objects) throws Exception;

}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.MethodUtils;

/**
 * Ajaxで有名なprototype.jsにある配列操作用の各種メソッド
 */
public abstract class Collections {

    public static final Exception BREAK = new Exception();
    public static final Exception CONTINUE = new Exception();

    private static boolean toBoolean(Object value) {
        if (value == null) {
            return false;
        }
        if (value instanceof Boolean) {
            return ((Boolean) value).booleanValue();
        }
        if (value instanceof String) {
            return Boolean.parseBoolean((String)value);
        }
        if (value instanceof Number) {
            return ((Number) value).intValue() != 0;
        }
        return true;
    }

    /**
     * コレクションの一部を抜き出す。
     * @param <V>
     * @param list
     * @param fromIndex
     * @param toIndex
     * @return
     */
    public static <V> Collection<V> slice(Collection<V> list, int fromIndex, int toIndex) {
        return new ArrayList<V>(new ArrayList<V>(list).subList(fromIndex, toIndex));
    }

    /**
     * コレクションの要素ごとにexecuter#execute()を呼び出す。
     * @param list
     * @param executer
     * @throws Exception
     */
    public static void each(Collection<?> list, Executer<?> executer) throws Exception {
        int index = 0;
        for(Iterator<?> i = list.iterator(); i.hasNext();) {
            try {
                executer.execute(index++, i.next());
            } catch (Exception e) {
                if (e == Collections.CONTINUE) {
                    continue;
                }
                if (e == Collections.BREAK) {
                    break;
                }
                throw e;
            }
        }
    }

    /**
     * コレクションの要素ごとにexecuter#execute()を呼び出し、その戻り値がすべてtrueか否かを返す。<br/>
     * いずれかの要素についてexecuter#execute()の戻り値がfalseであれば残りの要素については呼び出さない。<br/>
     * executerがnullであれば要素自体のtrue/falseを判定する。<br/>
     * @param list
     * @param executer
     * @return
     * @throws Exception
     */
    public static boolean all(Collection<?> list, final Executer<Boolean> executer) throws Exception {
        final boolean[] ret= new boolean[] {true};
        each(list, new Executer<Object>() {
            public Object execute(int index, Object... objects) throws Exception {
                if (!toBoolean(executer == null ? objects[0] : executer.execute(index, objects[0]))) {
                    ret[0] = false;
                    throw Collections.BREAK;
                }
                return null;
            }
        });
        return ret[0];
    }

    /**
     * コレクションの要素ごとにexecuter#execute()を呼び出し、戻り値がtrueのものがあったか否かを返す。<br/>
     * いずれかの要素についてexecuter#execute()の戻り値がtrueであれば残りの要素については呼び出さない。<br/>
     * executerがnullであれば要素自体のtrue/falseを判定する。<br/>
     * @param list
     * @param executer
     * @return
     * @throws Exception
     */
    public static boolean any(Collection<?> list, final Executer<Boolean> executer) throws Exception {
        final boolean[] ret = new boolean[] {false};
        each(list, new Executer<Object>() {
            public Object execute(int index, Object... objects) throws Exception {
                if (toBoolean(executer == null ? objects[0] : executer.execute(index, objects[0]))) {
                    ret[0] = true;
                    throw Collections.BREAK;
                }
                return null;
            }
        });
        return ret[0];
    }

    /**
     * コレクションの要素ごとにexecuter#execute()を呼び出し、その戻り値のコレクションを返す。
     * @param list
     * @param executer
     * @return
     * @throws Exception
     */
    public static <V> Collection<V> collect(Collection<?> list, final Executer<V> executer) throws Exception {
        final Collection<V> ret = new ArrayList<V>();
        each(list, new Executer<Object>() {
            public Object execute(int index, Object... objects) throws Exception {
                ret.add(executer.execute(index, objects[0]));
                return null;
            }
        });
        return ret;
    }

    /**
     * nullを除いたコレクションを返す。
     * @param <V>
     * @param list
     * @return
     * @throws Exception
     */
    public static <V> Collection<V> compact(Collection<V> list) throws Exception {
        return findAll(list, new Executer<Boolean>() {
            @Override
            public Boolean execute(int index, Object... objects) throws Exception {
                return objects[0] != null;
            }
        });
    }

    /**
     * コレクションの要素ごとにexecuter#execute()を呼び出し、最初にその戻り値がtrueである要素を返す。
     * @param <V>
     * @param list
     * @param executer
     * @return
     * @throws Exception
     */
    public static <V> V detect(Collection<V> list, final Executer<Boolean> executer) throws Exception {
        final List<V> ret = new ArrayList<V>();
        each(list, new Executer<Object>() {
            public Object execute(int index, Object... objects) throws Exception {
                if (executer.execute(index, objects[0])) {
                    ret.add((V)objects[0]);
                    throw Collections.BREAK;
                }
                return null;
            }
        });
        return ret.isEmpty() ? null : ret.get(0);
    }

    /**
     * コレクションの要素をnumsごとに複数のコレクションに分解し、分解したコレクションの要素ごとにexecuter#execute()を呼び出してその結果のコレクションを返す。<br/>
     * executerがnullであれば、分解した結果をそのまま返す。
     * @param <V>
     * @param <K>
     * @param list
     * @param nums
     * @param executer
     * @return
     * @throws Exception
     */
    public static <V, K> Collection<V> eachSlice(Collection<K> list, int nums, Executer<V> executer) throws Exception {
        int index = -nums;
        Collection<Collection<K>> tmpList = new ArrayList<Collection<K>>();
        while ((index += nums) < list.size()) tmpList.add(slice(list, index, Math.min(index + nums, list.size())));
        return (Collection<V>) (executer == null ? tmpList : collect(tmpList, executer));
    }

    /**
     * コレクションの要素ごとにexecuter#execute()を呼び出し、戻り値がtrueとなる要素のリストを返す。
     * @param <V>
     * @param list
     * @param executer
     * @return
     * @throws Exception
     */
    public static <V> Collection<V> findAll(Collection<V> list, final Executer<Boolean> executer) throws Exception {
        final Collection<V> ret = new ArrayList<V>();
        each(list, new Executer<Object>() {
            public Object execute(int index, Object... objects) throws Exception {
                if (executer.execute(index, objects[0])) {
                    ret.add((V)objects[0]);
                }
                return null;
            }
        });
        return ret;
    }

    /**
     * コレクションの中にコレクションが含まれていたら、内側のコレクションの要素を外側のコレクションの要素として内側のコレクション自身と置き換える。
     * @param <V>
     * @param list
     * @return
     * @throws Exception
     */
    public static <V> Collection<V> flatten(Collection<?> list) throws Exception {
        return (Collection<V>) inject(list, new ArrayList<V>(), new Executer<Collection<V>>() {
            @Override
            public Collection<V> execute(int index, Object... objects) throws Exception {
                Collection<V> ret = (Collection<V>) objects[1];
                if (objects[0] instanceof Collection<?>) {
                    ret.addAll((Collection<? extends V>) flatten((Collection<?>) objects[0]));
                } else {
                    ret.add((V)objects[0]);
                }
                return ret;
            }
        });
    }

    /**
     * コレクションの要素ごとに文字列表現がpatternにマッチするかどうかを調べ、マッチする要素についてexecuter#execute()を呼び出してその戻り値のリストを返す。<br/>
     * executerがnullであれば要素自体のリストを返す。
     * @param list
     * @param pattern 正規表現パターン 完全一致ではなくパターンに一致する部分があるか否かで判断する 完全一致にしたければパターンの前後に^$をつけること
     * @param executer
     * @return
     * @throws Exception
     */
    public static <V> Collection<V> grep(Collection<?> list, String pattern, final Executer<V> executer) throws Exception {
        final Collection<V> ret = new ArrayList<V>();
        final Pattern p = Pattern.compile(pattern);
        each(list, new Executer<Object>() {
            public Object execute(int index, Object... objects) throws Exception {
                if (objects[0] != null && p.matcher(objects[0].toString()).find()) {
                    ret.add((V) (executer == null ? objects[0] : executer.execute(index, objects[0])));
                }
                return null;
            }
        });
        return ret;
    }

    /**
     * {@link #eachSlice(Collection, int, Executer)}を呼び出し、最後のコレクションの要素数がnums未満の場合、不足分をfillWithで埋める。
     * @param <V>
     * @param list
     * @param nums
     * @param fillWith
     * @return
     * @throws Exception
     */
    public static <V> Collection<Collection<V>> inGroupsOf(Collection<V> list, final int nums, final V fillWith) throws Exception {
        return eachSlice(list, nums, new Executer<Collection<V>>() {
            @Override
            public Collection<V> execute(int index, Object... objects) throws Exception {
                Collection<V> list = (Collection<V>) objects[0];
                while (list.size() < nums) list.add(fillWith);
                return list;
            }
        });
    }

    /**
     * ある要素のexecuter#execute()の結果を次の要素のexecuter#execute()の引数として使用し、最後の要素のexecuter#execute()の結果を返す。<br/>
     * 先頭の要素に対するexecuter#execute()の引数はmemoを使用する。
     * @param <V>
     * @param list
     * @param memo
     * @param executer
     * @return
     * @throws Exception
     */
    public static <V> V inject(Collection<?> list, V memo, final Executer<V> executer) throws Exception {
        final List<V> tmp = new ArrayList<V>();
        tmp.add(memo);
        each(list, new Executer<Object>() {
            @Override
            public Object execute(int index, Object... objects) throws Exception {
                tmp.set(0, executer.execute(index, objects[0], tmp.get(0)));
                return null;
            }
        });
        return tmp.get(0);
    }

    /**
     * retainに含まれない要素を除いたコレクションを返す。
     * @param <V>
     * @param list
     * @param retain
     * @return
     * @throws Exception
     */
    public static <V> Collection<V> intersect(Collection<V> list, final Collection<V> retain) throws Exception {
        return findAll(list, new Executer<Boolean>() {
            @Override
            public Boolean execute(int index, Object... objects) throws Exception {
                return retain.contains(objects[0]);
            }
        });
    }

    /**
     * コレクションの要素ごとにメソッドを呼び出し、その結果をコレクションで返す。
     * @param <V>
     * @param list
     * @param method
     * @param arguments
     * @return
     * @throws Exception
     */
    public static <V> Collection<V> invoke(Collection<?> list, final String method, final Object... arguments) throws Exception {
        return collect(list, new Executer<V>() {
            @Override
            public V execute(int index, Object... objects) throws Exception {
                return (V)MethodUtils.invokeMethod(objects[0], method, arguments);
            }
        });
    }

    /**
     * コレクションの要素ごとにexecuter#execute()を呼び出し、その結果が最大となる要素を返す。
     * @param <V>
     * @param list
     * @param executer
     * @return
     * @throws Exception
     */
    public static <V extends Comparable<V>> V max(Collection<?> list, final Executer<V> executer) throws Exception {
        final List<V> ret = new ArrayList<V>();
        each(list, new Executer<Object>() {
            @Override
            public Object execute(int index, Object... objects) throws Exception {
                V tmp = executer.execute(index, objects);
                if (ret.isEmpty()) {
                    ret.add(tmp);
                } else {
                    if (ret.get(0).compareTo(tmp) < 0) {
                        ret.set(0, tmp);
                    }
                }
                return null;
            }
        });
        return ret.isEmpty() ? null : ret.get(0);
    }

    /**
     * コレクションの要素ごとにexecuter#execute()を呼び出し、その結果が最小となる要素を返す。
     * @param <V>
     * @param list
     * @param executer
     * @return
     * @throws Exception
     */
    public static <V extends Comparable<V>> V min(Collection<?> list, final Executer<V> executer) throws Exception {
        final List<V> ret = new ArrayList<V>();
        each(list, new Executer<Object>() {
            @Override
            public Object execute(int index, Object... objects) throws Exception {
                V tmp = executer.execute(index, objects);
                if (ret.isEmpty()) {
                    ret.add(tmp);
                } else {
                    if (ret.get(0).compareTo(tmp) > 0) {
                        ret.set(0, tmp);
                    }
                }
                return null;
            }
        });
        return ret.isEmpty() ? null : ret.get(0);
    }

    /**
     * コレクションの要素ごとにexecuter#execute()を呼び出し、その戻り値がtrueになる要素とfalseになる要素をそれぞれコレクションにして返す。<br/>
     * executerがnullであれば要素自体のtrue/falseを判定する。
     * @param <V>
     * @param list
     * @param executer
     * @return
     * @throws Exception
     */
    public static <V> Collection<Collection<V>> partition(Collection<V> list, final Executer<Boolean> executer) throws Exception {
        final Collection<V> trueList = new ArrayList<V>();
        final Collection<V> falseList = new ArrayList<V>();
        each(list, new Executer<Object>() {
            public Object execute(int index, Object... objects) throws Exception {
                if (toBoolean(executer == null ? objects[0] : executer.execute(index, objects[0]))) {
                    trueList.add((V)objects[0]);
                } else {
                    falseList.add((V)objects[0]);
                }
                return null;
            }
        });
        return Arrays.asList(trueList, falseList);
    }

    /**
     * すべての要素から指定したプロパティの値を取得してリストにして返す。
     * @param <V>
     * @param list
     * @param propertyName
     * @return
     * @throws Exception
     */
    public static <V> Collection<V> pluck(Collection<?> list, final String propertyName) throws Exception {
        final Collection<V> ret = new ArrayList<V>();
        each(list, new Executer<Object>() {
            public Object execute(int index, Object... objects) throws Exception {
                ret.add((V)BeanUtils.getProperty(objects[0], propertyName));
                return null;
            }
        });
        return ret;
    }

    /**
     * コレクションの要素ごとにexecuter#execute()を呼び出し、戻り値がfalseとなる要素のリストを返す。
     * @param <V>
     * @param list
     * @param executer
     * @return
     * @throws Exception
     */
    public static <V> Collection<V> reject(Collection<V> list, final Executer<Boolean> executer) throws Exception {
        final Collection<V> ret = new ArrayList<V>();
        each(list, new Executer<Object>() {
            public Object execute(int index, Object... objects) throws Exception {
                if (!executer.execute(index, objects[0])) {
                    ret.add((V)objects[0]);
                }
                return null;
            }
        });
        return ret;
    }

    /**
     * objectsに含まれるオブジェクトを除いたコレクションを返す。
     * @param <V>
     * @param list
     * @param objects
     * @return
     * @throws Exception
     */
    public static <V> Collection<V> withOut(Collection<V> list, V... objects) throws Exception {
        return withOut(list, Arrays.asList(objects));
    }

    /**
     * rejectに含まれるオブジェクトを除いたコレクションを返す。
     * @param <V>
     * @param list
     * @param reject
     * @return
     * @throws Exception
     */
    public static <V> Collection<V> withOut(Collection<V> list, final Collection<V> reject) throws Exception {
        return findAll(list, new Executer<Boolean>() {
            @Override
            public Boolean execute(int index, Object... objects) throws Exception {
                return !reject.contains(objects[0]);
            }
        });
    }

}