Java Collection filtering

0 votes
asked Nov 24, 2009 by omu

I have something like this:

public class Foo {
  public String id;
}

and

Vector<Foo> foos;

I need to get an object from the collection by id.

In C# I would do like this: foos.Where(o => o.id = 7)

What's the best way to do that in Java ?

11 Answers

0 votes
answered Nov 6, 2009 by mario-fusco

Give a look at lambdaj. It allows to manipulate, filter, sort, aggregate collections in a pseudo-functional and very readable way.

0 votes
answered Nov 24, 2009 by bozho
Collection.binarySearch(List<T extends Comparable>, T key);

You pass your collection, and the key (an id, or whatever), and the method returns your object. Your object's class must implement the Comparable interface.

Note: the collection must be sorted before calling binarySearch (Collections.sort(..))

0 votes
answered Nov 24, 2009 by jon-skeet

For a start, I'd suggest using ArrayList<Foo> rather than Vector<Foo> - ArrayList is almost always preferable to Vector.

Use the Google Collections API, in particular Iterables.filter. It's pretty clunky at the moment - you'll either need to set up a predicate beforehand, or use an anonymous inner class, due to the lack of lambda expressions. Also, Java doesn't have extension methods, so you'll call Iterables.filter(collection, predicate) rather than collection.filter(predicate). Both of these will be somewhat simplified in Java 7.

Note that using filter will find an Iterable<Foo> - if you just need the first match, use Iterables.find instead, which is the equivalent of Enumerable.First<T>(Func<T, bool>) in LINQ.

0 votes
answered Nov 24, 2009 by alexander-pogrebnyak

First of all, don't use Vector, use ArrayList

ArrayList< Widget > widgets = ...

Widget found = null;

for ( Widget o : widgets )
{
  if ( o.id == 7 )
  {
    found = o;
    break;
  }

}
0 votes
answered Nov 24, 2009 by brian-agnew

If you have an ArrayList (or similar - i.e. something from the Collections library) then Apache Commons Collections has lots of facilities for filtering/iterating etc.

Note that unlike the Google Collections library referenced in Jon's answer, there's no support for generics.

0 votes
answered Nov 24, 2009 by andreas-d

I think, the traditional way in Java is to iterate through the list and search for the Foo with the id you looked for (complexity O(n)). If that's to slow, you might consider using a HashMap structure where you map your foo to its index.

One could 'hide' the lookup by subclassing the collection class:

public class ListOfFoos extends ArrayList<Foo> {

  public Foo getFooByIndex(String index) {
    // do your lookup here
  }

}

and use ListOfFoos instead of ArrayList from now on as a new Collection type which allows direct acces to a Foo by its index number.

0 votes
answered Nov 24, 2009 by seh

The following types provide filtering over sequences. This solution is general but not well suited for sets or sorted sequences, each of which offer more efficient means to find and drop elements matching some exemplar.

First, define an Iterator type that's really a lazy generator adapter:

abstract class IteratorHusk<T> implements Iterator<T>
{
  @SuppressWarnings("unchecked")
  protected IteratorHusk()
  {
    value_ = nil();
  }


  @SuppressWarnings("unchecked")
  protected T nil()
  {
    return (T) NIL;
  }


  protected abstract T yield();


  private boolean tryPop()
  {
    value_ = yield();
    return NIL != value_;
  }


  @SuppressWarnings("unchecked")
  private T take()
  {
    final T current = value_;
    value_ = (T) NIL;
    return current;
  }


  public final boolean hasNext()
  {
    return NIL != value_ || tryPop();
  }


  public final T next()
  {
    if (NIL == value_ && !tryPop())
    {
      throw new NoSuchElementException();
    }
    return take();
  }


  public void remove()
  {
    throw new UnsupportedOperationException();
  }


  // We want to tolerate null as a possibly valid value.
  private static final Object NIL = new Object();
  private T value_;
}

It's 2009 and Java still lacks closures and first-class functions, so we sheepishly introduce this family:

interface UnaryFunction<T, U>
{
  T eval(U argument);
}

Now, wrap a generator around a unary predicate to build a sequence filter:

class FilteringIterator<T> extends IteratorHusk<T>
{
  public FilteringIterator(Iterator<? extends T> iter,
                           UnaryFunction<Boolean, ? super T> pred)
  {
    iter_ = iter;
    pred_ = pred;
  }


  @Override
  protected T yield()
  {
    while (iter_.hasNext())
    {
      final T val = iter_.next();
      if (!pred_.eval(val))
      {
        return val;
      }
    }
    return nil();
  }


  private final Iterator<? extends T> iter_;
  private final UnaryFunction<Boolean, ? super T> pred_;
}

Now, expose a convenience function:

public static <T>
Iterator<T> lazyFilter(UnaryFunction<Boolean, ? super T> pred,
                       Iterator<? extends T> source)
{
  return new FilteringIterator<T>(source, pred);
}
0 votes
answered Nov 24, 2009 by nd

With Google Collections, that would be:

Lists.newArrayList(Iterables.filter(foos, new Predicate<Foo>() {
  public boolean apply(Foo input) {
    return input != null && "7".equals(input.id);
  }
}));

Iterables.filter (and Collections2.filter, which does the same) gives you a live view on the filtered collection, just like seh's concept, but with less code. In order to create a list out of it again, I pass it to the newArrayList method of Google Collection's List utility class.

Just like everybody else, I would strongly suggest not use Vector as a declaration. Instead, try to use the most generic type possible, e.g., List<Foo> or even Collection<Foo>. Also, unless you need the synchronization feature of Vector, use ArrayList (or some other type suited for the problem).

0 votes
answered Nov 24, 2009 by kevin-bourrillion

You probably want to store your data in a Map<Integer, Foo> instead of a List<Foo>. A TreeMap, for instance, would keep everything in sorted order.

0 votes
answered Nov 26, 2015 by lstypka

Restrictions class from sweetener project solves this problem.

Example:

Collection<Foo> filteredList = Collections.filter(foos, Criteria.newCriteria().add(Restrictions.equals("id", 7)));

Other examples

Welcome to Q&A, where you can ask questions and receive answers from other members of the community.
Website Online Counter

...