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: Pakistan
The 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% Electric
Chapter 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.