4.4. Chaining Comparators

Problem

You need to sort a collection of objects by more than one property.

Solution

Use ComparatorChain to create a series of Comparator objects that appear as a single Comparator. A ComparatorChain compares two objects with each Comparator in the chain until a Comparator returns a nonzero value. If a Comparator returns equality (zero), the ComparatorChain then moves to the next Comparator to obtain another comparison. The following example demonstrates the use of chained BeanComparators; if two Person objects have the same lastName, the chain created in this example tries to compare the objects by firstName:

Person person1 = new Person( );
person1.setLastName( "Payne" );
person1.setFirstName( "James" );
person1.setAge( 21 );

Person person2 = new Person( );
person2.setLastName( "Payne" );
person2.setFirstName( "James" );
person2.setAge( 85 );

Person person3 = new Person( );
person3.setLastName( "Payne" );
person3.setFirstName( "Susan" );
person3.setAge( 29 );

Person[] persons = new Person[] { person1, person2, person3 };

ComparatorChain comparatorChain = new ComparatorChain( );
comparatorChain.addComparator( new BeanComparator( "lastName" ) );
comparatorChain.addComparator( new BeanComparator( "firstName" ) );
comparatorChain.addComparator( new BeanComparator( "age" ), true );

Arrays.sort( persons, comparatorChain );

This example sorts an array of Person objects: by lastName ascending, firstName ascending, and age descending. The sorted persons array will be in the following order: the older James Payne, the younger James Payne, followed by Susan Payne. The ChainedComparator successfully sorts two objects with the same first and last names—falling back to the age to provide the correct sort order.

Discussion

A ComparatorChain evaluates every Comparator in the chain as needed. If the current Comparator returns a zero, the next Comparator is invoked until there are no more Comparator objects left in the chain. If the final Comparator returns a zero value, the ComparatorChain returns a zero. The ComparatorChain implements the Comparator interface and is an aggregate of other Comparator objects; it can be used wherever a Comparator is used, including array sorting and as a Comparator for a tree-based Collection implementation.

The ReverseComparator (introduced in Recipe 4.3) makes more sense in light of the ComparatorChain. If you need to sort a collection of objects for display, you might want to reverse a particular Comparator in a ComparatorChain. In the previous example, there are two people named “James Payne”: one is 21 and the other is 85. Your application respects age and you want to put the older James Payne in front of the younger James Payne in the sorted list. Sorting by lastName ascending, firstName ascending, and age descending calls for the last Comparator in the chain to be reversed; the following code calls addComparator( ) with a second parameter, true, causing the BeanComparator for age to be reversed with ReverseComparator:

comparatorChain.addComparator( new BeanComparator( "age" ), true );

The previous statement is equivalent to the following code:

comparaterChain.addComparator( new ReverseComparator
                               (new BeanComparator("age") );

Using a ComparatorChain may remind you of the way a result set is sorted in a SQL SELECT statement. This recipe’s Solution implements the following SQL statement in a ComparatorChain:

SELECT * FROM person ORDER BY lastName ASC, firstName ASC, age DESC;

See Also

The BeanComparator was introduced in the previous chapter; for more information, see Recipe 3.10. The ReverseComparator was introduced in Recipe 4.3.

Get Jakarta Commons Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.