Subsorting with multiple Comparators I've seen a number of questions on Stack Overflow of the kind of "How do I sort on fields X AND Y?" The most common answers to those questions consists of "Write a custom Comparator."

But what if you have 3 fields you can sort on? Or 13? or 300? My solution is this utility class, so that rather than writing a billion different Comparators for every possible combination you might want, you write just 3 (or 13, etc.) simple ones and essentially composite them to get the desired behavior.

This method is designed to be used as essentially an expanded version of Collections.sort(List list, Comparator c) so I want to adhere to the same contract as much as possible.

I am also interested in ways to improve the documentation/API and finding possibly troublesome corner cases.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * A utility class for sorting a list using multiple sorting criteria.
 *
 */
public class Multisorter {
    /**
     * Sorts the given List using the given Comparators.
     * 
     * The List is sorted by the Comparators in the order the Comparators are
     * given; the elements are sorted according to the first Comparator, then
     * all of the elements that are considered "equal" are subsorted recursively
     * by the next Comparator and so on.
     * 
     * This method makes use of Collections.sort(List&lt;T&gt;, Comparator&lt;T&gt;)
     * internally and obeys the same contract.
     * 
     * @param &lt;T&gt;
     *            the generic type of the list.
     * 
     * @param list
     *            the List to sort
     * @param comparators
     *            the comparators to sort with
     * @return the sorted List
     */
    public static &lt;T&gt; void sort(List&lt;T&gt; list, Comparator&lt;? super T&gt;... comparators) {
        if (comparators.length &lt;= 0) {
            throw new IllegalArgumentException("Must provide at least one Comparator to sort with.");
        }
        Collections.sort(list, comparators[0]);
        if (comparators.length &gt; 1) {
            List&lt;List&lt;T&gt;&gt; subCollections = new ArrayList&lt;&gt;();
            for (T element : list) {
                boolean matchFound = false;
                for (List&lt;T&gt; subList : subCollections) {
                    if (comparators[0].compare(element, subList.get(0)) == 0) {
                        subList.add(element);
                        matchFound = true;
                    }
                    if (matchFound) {
                        break;
                    }
                }
                if (!matchFound) {
                    int lastIndex = subCollections.size();
                    subCollections.add(lastIndex, new ArrayList&lt;&gt;());
                    subCollections.get(lastIndex).add(element);
                }
            }

            // Sort each of the subcollections recursively without the first Comparator
            for (List&lt;T&gt; subList : subCollections) {
                sort(subList, Arrays.copyOfRange(comparators, 1, comparators.length));
            }
            // Aggregate all the subcollections
            list.clear();
            for (List&lt;T&gt; subList : subCollections) {
                list.addAll(subList);
            }
        }
    }
}


However, based on comments by Alnitak and rolfl, I would just use the new thenComparing method in Comparator added in Java 8 that I didn't know about.
I find your code fairly complicated. Especially the part where you split the list into sequences of sublists of equal elements is a bit convoluted. Here's what you're doing in pseudocode:

given a comparator,
for each element in the list:
    for each sublist:
       if per the comparator, the element is equal to an element in that sublist:
           add the element to the sublist,
           continue with next element
    if no sublist matched:
        add a new sublist containing that element


The most straightforward way to implement that in Java is using a labeled loop. Using that, we can continue with the next element in the outer loop once a match was found. We do not have to maintain a matchFound variable:

List&lt;List&lt;T&gt;&gt; subCollections = new ArrayList&lt;&gt;();

element:
for (T element : list) {
    for (List&lt;T&gt; subList : subCollections) {
        if (comparators[0].compare(element, subList.get(0)) == 0) {
            subList.add(element);
            continue element;
        }
    }
    // no sublist matched
    List&lt;T&gt; newList = new ArrayList&lt;&gt;();
    newList.add(element);
    subCollections.add(newList);
}


There's also no need to use a lastIndex here.

Unfortunately, what you are doing is tremendously inefficient. You re-sort each list multiple times, and invoke each comparator unnecessarily often. You are more or less implementing a crazy inefficient multi-level quicksort that partitions into multiple parts per level.

The better solution would be to create a custom comparator that loops through the list of comparators and stops when they are no longer equal:

class MultiComparator&lt;T&gt; implements Comparator&lt;T&gt; {
    private final List&lt;Comparator&lt;T&gt;&gt; comparators;

    public MultiComparator(List&lt;Comparator&lt;? super T&gt;&gt; comparators) {
        this.comparators = comparators;
    }

    public MultiComparator(Comparator&lt;? super T&gt;... comparators) {
        this(Arrays.asList(comparators));
    }

    public int compare(T o1, T o2) {
        for (Comparator&lt;T&gt; c : comparators) {
            int result = c.compare(o1, o2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }

    public static &lt;T&gt; void sort(List&lt;T&gt; list, Comparator&lt;? super T&gt;... comparators) {
        Collections.sort(list, new MultiComparator&lt;T&gt;(comparators));
    }
}


(untested)