5.4. Filtering a Collection with a Predicate

Problem

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.

Solution

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

Discussion

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( ).

See Also

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.173.238