You need to sort a collection of objects that have a preestablished order, such as the days of the week or the order of planets in the solar system.
Use FixedOrderComparator
in Jakarta Commons
Collections. When using FixedOrderComparator
, you
supply an array of objects in a sorted order and the
Comparator
returns comparison results based on the
order of the objects in this array. The following example uses a
fixed string array to compare different Olympic medals:
import java.util.*; import org.apache.commons.beanutils.BeanComparator; import org.apache.commons.collections.comparators.FixedOrderComparator; String[] medalOrder = {"tin", "bronze", "silver", "gold", "platinum"}; Comparator medalComparator = new FixedOrderComparator( medalOrder ); Comparator athleteComparator = new BeanComparator( "medal", medalComparator ); Athlete athlete1 = new Athlete( ); Athlete athlete2 = new Athlete( ); int compare = medalComparator.compare( athlete1.getMedal( ), athlete2.getMedal( ) );
In this code, a FixedOrderComparator
compares two
Athletes
by the value of the
medal
property. The medal
property can be “tin,”
“bronze,”
“silver,”
“gold,” or
“platinum,” and a
FixedOrderComparator
uses the order of the
medalOrder
array to compare these values. The
medalOrder
array establishes a fixed relationship
between the three medal types;
“bronze” is less than
“silver,” which is less than
“gold.”
Use FixedOrderComparator
when sorting an array or
a collection that contains values that are ordered in a
pre-determined fashion: days of the week, planets in the solar
system, colors in the spectrum, or hands dealt in a poker game. One
way to sort an array containing the days of the week would be to
assign a numerical value to each
day—“Monday” is one,
“Tuesday” is two,
“Wednesday” is three, etc. Then you
could sort the array with a Comparator
that takes
each day’s name, sorting elements based on the
numerical value corresponding to a day’s name. An
alternative is the use of FixedOrderComparator
,
letting the comparator order objects based on the order of an array
of day names.
If a bean contains a property to be sorted according to a fixed-order
array, you can use the BeanComparator
in
conjunction with FixedOrderComparator
. The
following example sorts cards by value and suit using a
FixedOrderComparator
and a
BeanComparator
; A PlayingCard
object, defined in Example 4-3, is sorted according
to the order of two arrays—one for the face value of the
PlayingCard
and one for the suit of the
PlayingCard
.
Example 4-3. A bean representing a playing card
package org.discursive.jccook.collections.compare; public class PlayingCard( ) { public static String JOKER_VALUE = null; public static String JOKER_SUIT = null; private String value; private String suit; public PlayingCard( ) {} public PlayingCard(String value, String suit) { this.value = value; this.suit = suit; } public String getValue( ) { return value; } public void setValue(String value) { this.value = value; } public String getSuit( ) { return suit; } public void setSuit(String suit) { this.suit = suit; } public String toString( ) { String cardString = "JOKER"; if( value != null && suit != null ) { cardString = value + suit; } return cardString; } }
Example 4-4 creates a
ComparatorChain
of
BeanComparators
, which compares the
value
and suit
properties using
a FixedOrderComparator
. Each
card’s suit is compared first, and, if two cards
have the same suit, they are compared by face value. Jokers do not
have suits or a face value, and this example handles jokers with a
null
-valued suit
and
value
property by wrapping each
FixedOrderComparator
with a
NullComparator
.
Example 4-4. Combining FixedOrderComparator with BeanComparator, NullComparator, and ComparatorChain
package com.discursive.jccook.collections.compare; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import org.apache.commons.beanutils.BeanComparator; import org.apache.commons.collections.comparators.NullComparator; import org.apache.commons.collections.comparators.FixedOrderComparator; import org.apache.commons.collections.comparators.ComparatorChain; public class FixedOrderExample { // Suit order "Spades", "Clubs", "Diamonds", "Hearts" private String[] suitOrder = { "S", "C", "D", "H" }; private String[] valueOrder = { "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A" }; public static void main(String[] args) { FixedOrderExample example = new FixedOrderExample( ); example.start( ); } public void start( ) { List cards = new ArrayList( ); cards.add( PlayingCard( "J", "C" ) ); cards.add( PlayingCard( "2", "H" ) ); cards.add( PlayingCard( PlayingCard.JOKER_VALUE, PlayingCard. JOKER_SUIT)); cards.add( PlayingCard( "2", "S" ) ); cards.add( PlayingCard( "Q", "S" ) ); cards.add( PlayingCard( "4", "C" ) ); cards.add( PlayingCard( "J", "D" ) ); System.out.println( "Before sorting: " + printCards( cards ) ); // Create a null-safe suit order comparator that will compare the // suit property of two Java beans Comparator suitComparator = new FixedOrderComparator( suitOrder ); suitComparator = new NullComparator( suitComparator ); suitComparator = new BeanComparator( "suit", suitComparator ); // Create a null-safe value order comparator that will compare the // value property of two Java beans Comparator valueComparator = new FixedOrderComparator( valueOrder ); valueComparator = new NullComparator( valueComparator ); valueComparator = new BeanComparator( "value", valueComparator );// Create a chain of comparators to sort a deck of cards
Comparator cardComparator = new ComparatorChain( );
cardComparator.addComparator( suitComparator );
cardComparator.addComparator( valueComparator );
Collections.sort( cards, cardComparator );
System.out.println( "After sorting: " + printCards( cards ) ); } private String printCards( List cards ) { StringBuffer resultBuffer = new StringBuffer( ); Iterator cardIter = cards.iterator( ); while( cardIter.hasNext( ) ) { PlayingCard card = (PlayingCard) cards.next( ); resultBuffer.append( " " + card.toString( ) ); } return resultBuffer.toString( ); } }
This example sorts the PlayingCard
objects and
produces the following output:
Before sorting: JC 2H JOKER 2S QS 4C JD After sorting: 2S QS 4C JC JD 2H JOKER
The list is sorted such that all the cards of a similar suit are
grouped together—spades are less than clubs, clubs are less
than diamonds, and diamonds are less than hearts. A sorted collection
of cards is grouped by suits, and, within each suit, cards are
organized according to face value—2 is low and aces is high.
The order used in the sorting is captured in two fixed-order arrays,
suitOrder
and faceOrder
. If a
shuffled deck were used in the example, it would end up as a
perfectly sorted deck of cards.
Example 4-4 ties a number of simple
Comparators
together to perform a fairly complex
sort. A FixedOrderComparator
is wrapped in a
NullComparator
, which is then wrapped with a
BeanComparator
. These
BeanComparator
instances are then combined in a
ComparatorChain
. The use of
NullComparator
with a
BeanComparator
is recommended to avoid a
NullPointerException
from
BeanComparator
; BeanComparator
is not designed to handle null
-valued bean
properties, and it throws an exception if you ask it to play nice
with null
s.
BeanComparator
is discussed in Recipe 3.10. This helpful utility is indispensable if
you are working with systems that need to sort JavaBeans.
For more information about the ComparatorChain
object, see Recipe 4.4. For more information on the
NullComparator
, see Recipe 4.5.
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.