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 ?
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 ?
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(..)
)
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.
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;
}
}
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.
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.
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);
}
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).
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.
Give a look at lambdaj. It allows to manipulate, filter, sort, aggregate collections in a pseudo-functional and very readable way.