HuntExams Academy logo
JavaIntermediate#collections

How do you sort a List of custom objects by multiple fields?

Answer

Use Comparator.comparing() chained with thenComparing() to define a primary sort key followed by tie-breaking keys, applied via List.sort() or Collections.sort().

Example

list.sort(Comparator.comparing(Person::getLastName)
  .thenComparing(Person::getFirstName));

Detailed explanation

To sort a List of custom objects by multiple fields in Java, build a Comparator that defines the fields in priority order. Use Comparator.comparing() for the primary field and thenComparing() for each tie-breaker. For example, list.sort(Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName)); sorts people by last name first, then by first name only when their last names compare as equal. These methods are available from Java 8 and let you express ordering without changing the Person class or defining its natural order through Comparable.

Under the hood, a comparator compares two objects and returns a negative number, zero, or a positive number to indicate their relative order. A chained comparator evaluates the primary comparison first; it evaluates the next comparator only if the previous result is zero. The default comparing() overload uses the extracted key's natural order, so String keys follow String.compareTo(), not locale-aware dictionary ordering. For primitive fields, use comparingInt(), comparingLong(), or comparingDouble(), and their thenComparing variants, to avoid boxing. For example, Comparator.comparing(Person::getLastName).thenComparingInt(Person::getAge) uses age as a numeric tie-breaker.

Sort direction and null handling must be explicit when the requirements need them. To sort last names ascending but ages descending, use Comparator.comparing(Person::getLastName).thenComparing(Comparator.comparingInt(Person::getAge).reversed()). Calling reversed() on the completed chain reverses every ordering criterion, not just the last one. For nullable names, use Comparator.comparing(Person::getLastName, Comparator.nullsLast(Comparator.naturalOrder())). This handles null field values; null Person elements require a separate outer Comparator.nullsLast() wrapper. Without these protections, comparing null keys or extracting fields from null objects can throw NullPointerException.

Apply the comparator with list.sort(comparator) or Collections.sort(list, comparator). Both modify the existing list, so it must support element replacement; List.of() lists do not. To preserve the original order, sort an ArrayList copy instead. List sorting is stable: elements that compare as equal across all configured fields retain their previous relative order. Interviewers typically check whether you understand tie-breaking, comparator consistency, mixed directions, and the difference between sorting an existing list and producing a separate sorted result.

Key points to remember

  • Use comparing() for the primary key and thenComparing() for successive tie-breakers.
  • Later fields are compared only when all earlier comparisons return zero.
  • Use primitive comparator methods such as thenComparingInt() for numeric fields.
  • Apply reversed() to an individual comparator for mixed ascending and descending ordering.
  • List.sort() modifies the list and preserves the relative order of elements that compare as equal.

Common mistakes candidates make

  • Calling reversed() on the entire chain when only one field should be descending.
  • Subtracting integers in a custom comparator, which can overflow; use Integer.compare() or comparingInt().
  • Assuming null field values and null list elements are handled automatically.

Likely follow-up questions

When should you use Comparator instead of Comparable?

Comparable defines a class's natural ordering through compareTo(). Comparator defines an external ordering, making it suitable for multiple sorting rules without modifying the class.

How do you create a sorted list without modifying the original?

Create a copy with List<Person> sorted = new ArrayList<>(list), then call sorted.sort(comparator). This copies the list structure, not the Person objects themselves.

Must a comparator return zero only when equals() returns true?

List sorting does not require comparison equality to match equals(), although the comparator must satisfy its ordering contract, including transitivity. For TreeSet and TreeMap, comparison equality determines duplicate elements or keys, so inconsistency with equals() can produce surprising behaviour.

Interview tip: Start with a two-field comparator example, explain when the tie-breaker runs, and then mention mixed directions or null handling.

Related Interview Questions

1
JavaBeginner#generics

What is the diamond operator in generics?

Open
2
JavaBeginner#exceptions

What is the exception hierarchy in Java?

Open
3
JavaBeginner#exceptions

What is a custom exception and how do you create one?

Open