views:

92

answers:

4

Given a starting List<Foo>, what is the most concise way to determine if a Foo element having a property bar (accessed by getBar()) has a value of "Baz"? The best answer I can come up with is a linear search:

List<Foo> listFoo;
for(Foo f:listFoo) {
    if(f.getBar().equals("Baz")) {
        // contains value
    }
}

I looked into HashSet but there doesn't seem to be a way to use contains() without first instantiating a Foo to pass in (in my case, Foo is expensive to create). I also looked at HashMap, but there doesn't seem to be a way to populate without looping through the list and adding each Foo element one at a time. The list is small, so I'm not worried about performance as much as I am clarity of code.

Most of my development experience is with C# and Python, so I'm used to more concise statements like:

// C#
List<Foo> listFoo;
bool contains = listFoo.Count(f => f.getBar=="Baz")>0;

or

# Python
# list_foo = [Foo(), ...]
contains = "Baz" in (f.bar for f in list_foo)

Does Java have a way to pull this off?

+2  A: 

You can only emulate this in Java, e.g. using a "function object". But since this is a bit awkward and verbose in Java, it is only worth the trouble if you have several different predicates to select elements from a list:

interface Predicate<T> {
  boolean isTrueFor(T item);
}

Foo getFirst(List<Foo> listFoo, Predicate<Foo> pred) {
  for(Foo f:listFoo) {
    if(pred.isTrueFor(f)) {
      return f;
    }
  }
}

class FooPredicateBar implements Predicate<Foo> {
  private final String expected;
  FooPredicateBar(String expected) {
    this.expected = expected;
  }
  public boolean isTrueFor(Foo item) {
    return item != null && expected.equals(item.getBar());
  }
}
...
List<Foo> listFoo;
Foo theItem = getFirst(listFoo, new FooPredicateBar("Baz"));
Péter Török
+3  A: 

In and of itself Java does not. Also (just as an fyi) f.getBar == "Baz" won't work for string comparison, due to the fact that strings are objects. Then you use the == operator you are actually comparing objects (which are not equal because they are not at the same memory location and are individual objects). The equals method is the best way to do object comparisons. And specifically it is best to "Baz".equals(f.getBar()) as this also avoids nasty NullPointerExceptions.

Now to address your question. I can think of ways to do it, but it probably depends on the relationship of the parent object Foo to the child object Bar. Will it always be one to one or not? In other words could the Bar value of "Baz" be associated with more than one Foo object?

Where I'm going with this is the HashMap object that you talked about earlier. This is because there are the methods containsKey and containsValue. Since HashMap does allow duplicate values associated with different keys, you could put Bar as the value and Foo as the key. Then just use myHashMap.containsValue("Baz") to determine if it is in "the list". And since it is, then you can always retrieve the keys (the Foos) that are associate with it.

Chris Aldrich
*"due to strings being immutable"* is actually not true. It's because strings are objects, not primitives and the `==` compares objects by references. The immutability of strings would only be a "problem" for the case one wonders why `string.replace("foo", "bar")` don't work (the answer would be that string methods doesn't (can't) change the current string but returns a new string and that you should reassign it).
BalusC
Correct. Was getting ahead of myself. Edited my post to correct this. :)
Chris Aldrich
+4  A: 

Java does not support closures (yet), so your solution is one of the shortest. Another way would be to use, for example, google-collections Iterable's closure-like Predicate:

boolean contains = Iterables.any(iterableCollection, new Predicate<Foo>() {
    @Override
    public boolean apply(Foo foo) {
       return foo != null && foo.getBar().equals("Baz");
    }
}
Bozho
+2  A: 

You can also use Apache Commons CollectionUtils:

 boolean contains = CollectionUtils.exists(listFoo, new Predicate() {
     public boolean evaluate(Object input) {
         return "Baz".equals(((Foo)input).getBar());
     }
 });
dogbane