Void Main
Void Main

Reputation: 2291

Better ways to add a "filter" method to a list of elements?

I want to implement a filter method to a list of elements in java, so I can get rid of some elements in the list according to my filter. And, most important, I want to design the interface as simple as possible.

Here's my implementation: I created a class named EasierList, and in the class, I added a method whose signature and implementation is like below:

public IEasierList<T> filter(ISelection<T> filter) {
    List<T> result = new ArrayList<T>();
    for(T item : mInternalList) {
        if(filter.accept(item)) {
            result.add(item);
        }
    }

    mInternalList = result;
    return new EasierList<T>(this);
}

As for the ISelection interface, it is quite a easy one:

public boolean accept(T obj);

So, you can tell, the users who use this class need to write some code like this to use the filter:

aEasierList.filter(new ISelection<T>() {
@Override
public boolean accept(T obj) {
        // some test
    return false;
}
});

And I'm wondering if there is a better way to do this, I mean to make the interface even easier to use?

Thanks in advance!

Upvotes: 1

Views: 270

Answers (2)

Natix
Natix

Reputation: 14247

Two points:

First, you don't have to reinvent the wheel, you can use Guava which already supports filtering and transforming of collections and iterables. Sure, those methods are static, but you can use them for standard List, Collection or Iterable interfaces.

Second, since Java doesn't yet support lambda expressions (planned for Java 8), the verbose anonymous classes are the only way how to implement a function object (if you don't want to create a full blown named class). However, you can help yourself a little by not implementing the anonymous class in-place, but by storing it in a static field:

private static final Predicate<String> startsWithS = new Predicate<String>() {
    @Override public boolean apply(String string) {
        return string.startsWith("S");
    }
}

And then using it like this:

Collection<String> strings = ...
Collection<String> filtered = Collections2.filter(strings, startsWithS);

Edit:

One more important thing should be mentioned: These filter and transform methods do not create a new collection independent on the original. What they create is a "view", which is technically a proxy object that points to the original collection and lazily applies the given Predicate or Function on its elements during iteration, querying etc.

This is sometimes convenient, but you have to remember, that in order to obtain a new collection that is independent (not deeply, of course) on the original one, you need to pass it to a constructor (or a factory) of a new collection.

List<String> filteredList =
    new ArrayList<>(Collections2.filter(strings, startsWithS));

By the way, this might be a good occasion to use static import for the filter method to reduce the verbosity a little.

Upvotes: 5

wrschneider
wrschneider

Reputation: 18770

I think this is the best you can do in native Java without resorting to an alternate JVM language (Groovy, Scala) that supports closures.

Commons Collections implements essentially the same pattern - check out docs for CollectionUtils.filter and Predicate. The only downside is it doesn't support generics.

http://commons.apache.org/collections/apidocs/org/apache/commons/collections/CollectionUtils.html

So if you're going that route, you may as well use something that's already written and tested.

C# also has a good solution to this pattern via LINQ and extension methods, which makes something analogous to the above filter method appear like it belongs to the Collection itself.

Upvotes: 3

Related Questions