3

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 ?

2
  • 1
    Are you looking to accommodate sequences only or any Collection type? If only sequences, can you assume the sequence is sorted? The algorithms suited to each and the time complexity costs are different. To apply one solution to all types of Collections would be doing a disservice to at least one of them. Commented Nov 24, 2009 at 12:39
  • yes, the ids are sorted they come like 1,2,3 although they are string Commented Nov 24, 2009 at 12:47

11 Answers 11

11

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.

Sign up to request clarification or add additional context in comments.

4 Comments

the problem with filtering functions is that the iterator goes through all the elements of the collection, no matter where the object you are searching is. You want to exit the loop as soon as you find the object.
@LucaFagioli: Admittedly Iterables is now somewhat discouraged compared with more modern Java streams, but my understanding is that Iterables.filter is still lazy - you could just take the first element of the returned iterable, and that would be fine.
Yes you can, but what I mean is that to return the iterator, the function still needs to inspect all the elements of the collection. From the documentation: Returns a view of unfiltered containing all elements that are of the type desiredType. This leads to an average case complexity of O(n), while manually searching for the object the worst case complexity is of O(n).
@Luca: No it doesn't. It returns an iterator that has a reference to the original iterator. Asking the filtered iterator for the next element involves iterating over the original one until the first match is found, that's all. It's O(n) to iterate over all the elements, but that doesn't mean what you seem to think it does.
6

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

Comments

5

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.

Comments

3

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;
  }

}

3 Comments

i've been told that Vectors are thread safe, and ArrayList not
Don’t listen to whoever said that, they’re obvisouly an idiot.
@Omu - all the methods on a Vector are synchronised, so there is some thread safety. However 1) you may not need this 2) you often want multiple operations to be synchronised as one - e.g. add/remove 3) the Collections class can easily provide synchronisation
1
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(..))

1 Comment

And the List will need to be sorted.
1

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.

Comments

1

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.

Comments

1

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

Comments

0

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);
}

2 Comments

it would be silly to write all that code yourself, then have to unit test it all (which is even harder than writing it, trust me).. just use a library.
This is part of a larger library that I wrote for internal use. There are several generators used in concert with the IteratorHusk type. Filtering a wrapped Iterator is just one of them.
0

Restrictions class from sweetener project solves this problem.

Example:

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

Other examples

Comments

0

If your collection is already sorted, you can take advantage of the binary search that gives you a worst case complexity of O(log n):

Collection.binarySearch(List<T extends Comparable>, T key);

If you are free to choose data structure, employ an HashMap<String, Object> which gives you a complexity of O(1).

ps: use ArrayList instead of Vector.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.