views:

311

answers:

9

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 ?

+1  A: 
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(..))

Bozho
*And* the List will need to be sorted.
Bart Kiers
Won't that work only if the `Vector` is sorted by `id`?
abyx
yup, added that.
Bozho
+8  A: 

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.

Jon Skeet
+3  A: 

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

}
Alexander Pogrebnyak
So this scans only the first item? (your `break` is wrong).
BalusC
You probably meant to put the `break` inside the `if` statement...
Bart Kiers
i've been told that Vectors are thread safe, and ArrayList not
Omu
@All - corrected the code example
Brian Agnew
Don’t listen to whoever said that, they’re obvisouly an idiot.
Bombe
@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
Brian Agnew
@Brian, thanks for correction.
Alexander Pogrebnyak
@Alexander - no problem
Brian Agnew
+1  A: 

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.

Brian Agnew
+1  A: 

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.

Andreas_D
A: 

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);
}
seh
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.
Kevin Bourrillion
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.
seh
+3  A: 

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

nd
+3  A: 

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.

Kevin Bourrillion
+1  A: 

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

Mario Fusco