Often, you must iterate through a collection of objects and filter them based on a number of criteria. The JDK supplies a
useful mechanism for sorting collections, namely the Comparator interface. However, the JDK lacks a mechanism for filtering collections.
This article describes a simple mechanism consisting of only one class and one interface that allows you to filter collections
quickly and neatly. When searching a collection, the described mechanism offers the same functionality as a SQL SELECT statement. Its underlying concept is its separation of responsibilities between iterating through the collection and filtering
the objects in the collection.
The approach presented here has the following benefits:
Imagine a search mask where a user can choose among numerous different criteria to search for cars. Approaching this task simply, the developer must iterate through the collection multiple times. In each iteration, he must execute certain logic on each object in the collection to decide whether it fits the criteria. Usually, the result of this process is messy code that is both hard to read and maintain.
We define a class called CollectionFilter and an interface called FilterCriteria.
FilterCriteria defines only one method: public boolean passes(Object o). In this method, an object in the collection must pass a certain test. If it passes the test, the method returns true, otherwise,
false.
CollectionFilter now takes any number of FilterCriteria as input. You then call the public void filter(Collection) method, which applies all FilterCriteria to the supplied collection and removes any object in the collection that doesn't pass all FilterCriteria.
The CollectionFilter class also defines a public Collection filterCopy(Collection) method, which completes the same task as the filter(Collection) method, but on a copy of the original filter.
That's it!
As you may have noticed, this solution borrows some ideas from the Chain of Responsibility design pattern and applies them to a collection.
The following class diagram illustrates the classes and interfaces and how they relate to each other.
Class diagram. Click on thumbnail to view full-sized image.
Let's look at an example: Class Car has three attributes: String color, double maxSpeed, boolean fourWheelDrive.
Your application allows searching for cars based on these criteria. The user can enter the color she prefers. She can also provide the maximum speed she wants the car to have and also whether the car should support four-wheel drive.
We now create three filter classes, one for each criteria the user can choose.
FilterCriteria implementations:class ColorFilterCriteria implements FilterCriteria{
private String color;
public boolean passes(Object o){
return ((Car)o).getColor().equals(color);
}
}
class MaxSpeedFilterCriteria implements FilterCriteria{
private int maxSpeed;
public boolean passes(Object o){
return ((Car)o).getMaxSpeed() >= maxSpeed;
}
}
class FourWheelDriveFilterCriteria implements FilterCriteria{
private boolean fourWheelDriveRequired;
private boolean fourWheelDriveAllowed;
public boolean passes(Object o){
return fourWheelDriveRequired?((Car)o).isFourWheelDrive():fourWheelDriveAllowed?true:!
((Car)o).isFourWheelDrive();
}
}
FilterCriteria to a CollectionFilter:CollectionFilter collectionFilter = new CollectionFilter(); filter.addFilterCriteria(new ColorFilterCriteria(color)); filter.addFilterCriteria(new MaxSpeedFilterCriteria(maxSpeed)); filter.addFilterCriteria(new FourWheelDriveFilterCriteria(fourWheelDriveRequired, fourWheelDriveAllowed));
collectionFilter.filter(carCollection);
As you may have realized, similar to the compare(Object o1, Object o2) method in the Comparator interface, the passes(Object o) method in the FilterCriteria interface takes an object of type Object as input. This means you must cast the object to the type you want to work with and ensure your collection only contains
an object of that type. If this is not certain, you can use instanceof to test whether the specific object is of that type.
Archived Discussions (Read only)