Reputation: 2291
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
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
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