Use BeanComparator to compare two beans using a bean property, which can be
simple, nested, indexed, or mapped. BeanComparator obtains the value of the same
bean property from two objects and, by default, compares the properties
with a ComparableComparator. The
following example sorts a list of Country objects using a BeanComparator and the default ComparableComparator:
import java.util.*;
import org.apache.commons.beanutils.BeanComparator;
Country country1 = new Country( );
country1.setName( "India" );
Country country2 = new Country( );
country2.setName( "Pakistan" );
Country country3 = new Country( );
country3.setName( "Afghanistan" );
// Create a List of Country objects
Country[] countryArray = new Country[] { country1, country2, country3 };
List countryList = Arrays.asList( countryArray );
// Sort countries by name
Comparator nameCompare = new BeanComparator( "name" );
Collections.sort( countryList, nameCompare );
System.out.println( "Sorted Countries:" );
Iterator countryIterator = countryList.iterator( );
while( countryIterator.hasNext( ) ) {
Country country = (Country) countryIterator.next( );
System.out.println( "\tCountry: " + country.getName( ) );
}This code creates three Country
objects with different names, places the Country objects into a list, and sorts this
list with a BeanComparator configured
to compare by the bean property name.
This code executes and prints the sorted list of countries:
Sorted Countries:
Country: Afghanistan
Country: India
Country: PakistanThe previous example demonstrated the default behavior of BeanComparator; when a BeanComparator is constructed with only one
parameter, it uses a ComparableComparator to compare the values of
the properties it retrieves. You can also construct a BeanComparator with an instance of Comparator; in this case, BeanComparator decorates another Comparator, retrieving the values of a
property and passing these values on to an instance of Comparator. The following example demonstrates
the use of BeanComparator with a
custom Comparator implementation.
This example involves two objects shown in Figure 3-6: ElectricVehicle and Engine.
An application needs to sort ElectricVehicle objects by efficiency, and, in
this contrived example, efficiency is defined as the number of miles per
gallon times the percentage of electric operation; an 80% electric
hybrid vehicle is more efficient than a 25% electric hybrid vehicle with
the same mileage because of reduced emissions. The code fragments shown
in Example 3-5 sort a collection of
beans by wrapping a custom Comparator
with a BeanComparator.
Example 3-5. Decorating a Comparator with a BeanComparator
import java.util.*;
import org.apache.commons.beanutils.BeanComparator;
// Create Engines
Engine engine1 = new Engine( );
engine1.setMilesGallon( new Integer(60) );
engine1.setPercentElectric( new Integer(50) );
Engine engine2 = new Engine( );
engine2.setMilesGallon( new Integer(90) );
engine2.setPercentElectric( new Integer(50) );
Engine engine3 = new Engine( );
engine3.setMilesGallon( new Integer(65) );
engine3.setPercentElectric( new Integer(45) );
// Create Vehicles
ElectricVehicle vehicle1 = new ElectricVehicle( );
vehicle1.setMake( "Toy Yoda" );
vehicle1.setModel( "Electro" );
vehicle1.setYear( 2005 );
vehicle1.setEngine( engine1 );
ElectricVehicle vehicle2 = new ElectricVehicle( );
vehicle2.setMake( "Fjord" );
vehicle2.setModel( "Photon" );
vehicle2.setYear( 2004 );
vehicle2.setEngine( engine2 );
ElectricVehicle vehicle3 = new ElectricVehicle( );
vehicle3.setMake( "Ford" );
vehicle3.setModel( "Electric Pinto" );
vehicle3.setYear( 2005 );
vehicle3.setEngine( engine3 );
// Create List of Vehicles
List vehicles = new ArrayList( );
vehicle.add( vehicle1 );
vehicle.add( vehicle2 );
vehicle.add( vehicle3 );
// Define Engine Comparison Logic in an Anonymous inner class
// which implements the Comparator interface
Comparator engineCompare = new Comparator( ) {
public int compare(Object o1, Object o2) {
Engine engine1 = (Engine) o1;
Engine engine2 = (Engine) o2;
int engine1Temp = engine1.getMilesGallon( ).intValue( ) *
engine1.getPercentElectric( ).intValue( );
int engine2Temp = engine2.getMilesGallon( ).intValue( ) *
engine2.getPercentElectric( ).intValue( );
Integer engine1Factor = new Integer( engine1Temp );
Integer engine2Factor = new Integer( engine2Temp );
return engine1Factor.compareTo( engine2Factor );
}
}
Comparator vehicleCompare = new BeanComparator( "engine", engineCompare );
Collections.sort( vehicles, vehicleCompare );
// Print Sorted Results
System.out.println( "Vehicles Sorted by Efficiency:" );
Iterator vehicleIter = vehicles.iterator( );
while( vehicleIter.hasNext( ) ) {
ElectricVehicle vehicle = (ElectricVehicle) vehicleIter.next( );
System.out.println( "\tVehicle: " + vehicle.getModel( ) + ", " +
vehicle.getEngine( ).getMilesGallon( ) + " MPG, " +
vehicle.getEngine( ).getPercentElectric( ) + "% Electric" );
}engineCompare contains the
logic used to sort vehicles by efficiency, and BeanComparator supplies the engine properties to this Comparator implementation. This previous
example creates three vehicles and sorts the vehicles in order of
efficiency; the following results are printed:
Vehicles Sorted by Efficiency:
Vehicle: Photon, 90 MPG, 50% Electric
Vehicle: Electro, 60 MPG, 50% Electric
Vehicle: Electric Pinto, 65 MPG, 45% ElectricChapter 4 provides examples of
various Comparator implementations,
which can be used to decorate other comparators. One Comparator in particular is important if you
plan to sort beans on a bean property, which could be null. If a bean property could be null, make sure to pass a NullComparator to BeanComparator; otherwise, ComparableComparator will throw a NullPointerException if a property value is
null. Recipe 4.5 discusses
techniques for decorating a Comparator with NullComparator.
Recipe 3.15
discusses a BeanPredicate object that can be used to validate beans. The BeanPredicate is similar to the BeanComparator as it decorates another
instance of Predicate, providing
access to a
bean
property.
