ArrayComparator

import java.util.Comparator;

/**
 * 配列の比較に使用するComparator。
 *
 * @param <V>
 */
public class ArrayComparator<V> implements Comparator<V[]> {

    // 比較する要素の優先順位
    private int[] priority;
    // すべての要素の比較に使用するComparator
    private Comparator<V> c;
    // 個々の要素の比較に使用するComparator
    private Comparator<V>[] comparators;

    /**
     * 先頭から順にすべての要素を比較する。
     * 要素は{@link Comparable}を実装しなければならない。
     */
    public ArrayComparator() {}

    /**
     * 指定したComparatorを使用してすべての要素を比較する。
     * @param comparator
     */
    public ArrayComparator(Comparator<V> comparator) {
        this(comparator, null);
    }

    /**
     * 指定したComparatorを使用して、指定された位置の要素を比較する。
     * @param comparator 共通のComparator
     * @param priority 比較を行う要素のインデックスの配列
     */
    public ArrayComparator(Comparator<V> comparator, int[] priority) {
        c = comparator;
        this.priority = priority;
    }

    /**
     * 要素ごとに指定したComparatorを使用して比較を行う。
     * comparators内にnullがあると、それに対応する配列の要素は{@link Comparable}を実装しているものとして比較を行う。
     * @param comparators
     */
    public ArrayComparator(Comparator<V>[] comparators) {
        this(comparators, null);
    }

    /**
     * 指定したComparatorを使用して、指定された位置の要素を比較する。
     * comparators内にnullがあると、それに対応する配列の要素は{@link Comparable}を実装しているものとして比較を行う。
     * @param comparators 要素ごとの比較に使用するComparator
     * @param priority 比較を行う要素のインデックスの配列
     */
    public ArrayComparator(Comparator<V>[] comparators, int[] priority) {
        if (priority != null && comparators.length != priority.length) {
            throw new IllegalArgumentException("comparators.length != priority.length");
        }
        this.comparators = comparators;
        this.priority = priority;
    }

    @Override
    public int compare(V[] o1, V[] o2) {
        // 両方nullなら同値
        if (o1 == null && o2 == null) {
            return 0;
        }
        // どちらかがnullならnullを小さいとみなす
        if (o1 == null) {
            return -1;
        }
        if (o2 == null) {
            return 1;
        }
        int l1 = o1.length;
        int l2 = o2.length;
        int ret = 0;
        // 優先順位に従って配列の要素を比較
        // 優先順位の指定が無ければ先頭からすべての要素を比較
        int length = priority != null ? priority.length : Math.min(o1.length, o2.length);
        for (int i = 0; i < length; i++) {
            V v1 = null;
            V v2 = null;
            if (priority != null) {
                // 優先順位の指定があれば、そのインデックスの要素を比較する
                if (priority[i] >= l1) {
                    throw new ArrayIndexOutOfBoundsException(i);
                }
                v1 = o1[priority[i]];
                if (priority[i] >= l2) {
                    throw new ArrayIndexOutOfBoundsException(i);
                }
                v2 = o2[priority[i]];
            } else {
                // 優先順位の指定が無ければループカウンタをそのまま配列のインデックスとみなす
                v1 = o1[i];
                v2 = o2[i];
            }
            if (c == null && (comparators == null || i >= comparators.length || comparators[i] == null)) {
                // 使用するComparatorがなければ要素自身に比較させる
                if (v1 != null) {
                    if (!(v1 instanceof Comparable)) {
                        throw new ClassCastException("object must implements Comparable");
                    }
                    ret = ((Comparable<V>) v1).compareTo(v2);
                } else if (v2 != null) {
                    if (!(v2 instanceof Comparable)) {
                        throw new ClassCastException("object must implements Comparable");
                    }
                    ret = ((Comparable<V>) v2).compareTo(v1) * -1;
                }
            } else if (c == null) {
                // 要素ごとのComparatorがあればそれを使用する
                ret = comparators[i].compare(v1, v2);
            } else {
                // 共通のComparatorを使用する
                ret = c.compare(v1, v2);
            }
            if (ret != 0) {
                return ret;
            }
        }
        // 優先順位の指定が無ければ配列の長さで大小を決める
        if (priority == null) {
            return l1 - l2;
        }
        // 優先順位の指定があれば指定された範囲の比較で大小が決まらなかったことになるので同値とする
        return 0;
    }
}