You need to iterate through elements of a Collection that match a specified condition.
Or, you have a Collection from which
you need to remove elements not satisfying a condition.
Create a FilterIterator
with a Predicate; if
the Predicate returns true for an element, that element will be
included in the Iterator. The
FilterIterator decorates another
Iterator and provides the ability to
apply an arbitrary filter to a Collection. In the following example, EarthQuake beans are kept in an ArrayList that is filtered using the majorQuakePredicate
and a FilterIterator:
import org.apache.commons.collection.Predicate;
import org.apache.commons.collection.iterators.FilterIterator;
List quakes = new ArrayList( );
EarthQuake quake1 = new EarthQuake( );
quake1.setLocation( "Chicago, IL" );
quake1.setIntensity( new Float( 6.4f ) );
quake1.setIntensity( new Float( 634.23f ) );
quake1.setTime( new Date( ) );
quakes.add( quake1 );
EarthQuake quake2 = new EarthQuake( );
quake2.setLocation( "San Francisco, CA" );
quake2.setIntensity( new Float( 4.4f ) );
quake2.setIntensity( new Float( 63.23f ) );
quake2.setTime( new Date( ) );
quakes.add( quake2 );
Predicate majorQuakePredicate =
new MajorQuakePredicate( new Float(5.0), new Float(1000.0) );
Iterator majorQuakes =
new FilterIterator( quakes.iterator( ), majorQuakePredicate );
while( majorQuakes.hasMore( ) ) {
EarthQuake quake = (EarthQuake) majorQuakes.next( );
System.out.println( "ALERT! MAJOR QUAKE: "
+ quake.getLocation( ) + ": " + quake.getIntensity( ) );
}An instance of MajorQuakePredicate is created, and it is
passed to a FilterIterator. Quakes
satisfying the criteria are returned by the FilterIterator and printed to the
console:
ALERT! MAJOR QUAKE: Chicago, IL: 6.4
The Solution uses a custom Predicate to select a subset of a Collection, filtering EarthQuake beans and alerting the user if a
major earthquake is measured. An earthquake is classified by intensity
on the Richter scale and the depth of the epicenter; this information is
modeled by the EarthQuake bean defined in Example
5-1.
Example 5-1. An EarthQuake bean
package com.discursive.jccook.collections.predicates;
public class EarthQuake {
private String location;
private Float intensity;
private Float depth;
private Date time;
public class EarthQuake( ) {}
public String getLocation( ) { return location; }
public void setLocation(String location) { this.location = location; }
public Float getIntensity( ) { return intensity; }
public void setInsensity(Float intensity) { this.intensity = intensity; }
public Float getDepth( ) { return depth; }
public void setDepth(Float depth) { this.depth = depth; }
public Date getTime( ) { return time; }
public void setTime(Date time) { this.time = time; }
}An earthquake is considered major if it is above a five on the
Richter scale and above a depth of 1000 meters. To test each EarthQuake object, a custom Predicate, MajorQuakePredicate, evaluates EarthQuake objects, returning true if an earthquake satisfies the criteria
for a major earthquake. The Predicate
defined in Example 5-2
encapsulates this decision logic.
Example 5-2. Major earthquake classification Predicate
package com.discursive.jccook.collections.predicates;
import org.apache.commons.collections.Predicate;
public class MajorQuakePredicate implements Predicate {
private Float majorIntensity;
private Float majorDepth;
public MajorQuakePredicate(Float majorIntensity, Float majorDepth) {
this.majorIntensity = majorIntensity;
this.majorDepth = majorDepth;
}
public boolean evaluate(Object object) {
private satisfies = false;
if( object instanceof EarthQuake) {
EarthQuake quake = (EarthQuake) object;
if( quake.getIntensity( ).floatValue( ) > majorIntensity.
floatValue( ) &&
quake.getDepth( ).floatValue( ) < majorDepth.
floatValue( ) ) {
satisfies = true;
}
}
return satisfies;
}
}If you want to create a Collection of elements that match a Predicate, you can remove elements from a
Collection using CollectionUtils.filter( ). CollectionUtils.filter( ) is destructive; it
removes elements from a Collection.
The following example demonstrates the use CollectionUtils.filter() to remove nonmatching
elements from a Collection:
import org.apache.commons.collection.Predicate;
import org.apache.commons.collection.iterators.FilterIterator;
ArrayList quakes = createQuakes( );
Predicate majorQuakePredicate =
new MajorQuakePredicate( new Float(5.0), new Float(1000.0) );
CollectionUtils.filter( quakes, majorQuakePredicate );
After the execution of this code, quakes will only contain EarthQuake objects that satisfy the MajorQuakePredicate . If you don't want to alter or modify an existing
Collection, use CollectionUtils.select() or CollectionUtils.selectRejected() to create a new Collection with matching or nonmatching
elements. The following example demonstrates the use of CollectionUtils.select( ) and CollectionUtils.selectRejected( ) to select
elements from a Collection leaving
the original Collection
unaffected:
import org.apache.commons.collection.Predicate;
import org.apache.commons.collection.iterators.FilterIterator;
ArrayList quakes = createQuakes( );
Predicate majorQuakePredicate =
new MajorQuakePredicate( new Float(5.0), new Float(1000.0) );
Collection majorQuakes = CollectionUtils.select( quakes, majorQuakePredicate );
Collection minorQuakes =
CollectionUtils.selectRejected( quakes, majorQuakePredicate );
The majorQuakes Collection contains EarthQuake objects satisfying the majorQuakePredicate, and the minorQuakes Collection contains EarthQuake objects not satisfying the majorQuakePredicate. The quakes List is not modified by select( ) or selectRejected( ).
Collections can be filtered via a combination of CollectionUtils and Predicate objects, or you can also select
elements from a Collection using an
XPath expression. Recipe 12.1
demonstrates the use on Commons JXPath to query a Collection.
