views:

234

answers:

4

In C++, I can use find_if with a predicate to find an element in a container. Is there something like that in Java? The contains method on collections uses equals and can not be parameterized.

+9  A: 

You can use Predicate from Google Collections. Here is the tutorial and an example from it:

final Predicate<Car> expensiveCar = new Predicate<Car>() {
   public boolean apply(Car car) {
      return car.price > 50000;
   }
}

List<Car> cars = Lists.newArrayList();
cars.add(new Car("Ford Taurus", 20000));
cars.add(new Car("Tesla", 90000));
cars.add(new Car("Toyota Camry", 25000));
cars.add(new Car("McClaren F1", 600000));

finalList<Car> premiumCars =
   Lists.immutableList(Iterables.filter(cars, expensiveCar));

You can also look at this thread: What is the best way to filter a Collection?

Roman
`Iterables.find` (http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Iterables.html#find%28java.lang.Iterable,%20com.google.common.base.Predicate%29) will provide just the first matching element.
Matthew Flaschen
@Matthew Flaschen: yes, if we only need to get at least 1 correspondent item then `Iterables.find` would be the best solution.
Roman
@Roman, I think you're using a very old pre-release snapshot of Google Collections! If you want to copy the filtered iterable into an immutable list (as opposed to just looping through it directly), use ImmutableList.copyOf().
Kevin Bourrillion
@Kevin Bourrillion: you're probably right, but it's just a piece of code from the tutorial. It can be used as a good start point rather then as code to copy from here and paste to your program. I've also found some differences in current Google Collections API and in its version used in the tutorial. But all concepts are still actual.
Roman
+1  A: 

You can use CollectionUtils.select from Apache Commons.

For example, the following C++ code

  bool isOdd (int i) {
    return i % 2 != 0;
  }
  ...
  vector<int> myvector;
  vector<int>::iterator it;

  myvector.push_back(10);
  myvector.push_back(25);
  myvector.push_back(40);
  myvector.push_back(55);

  it = find_if (myvector.begin(), myvector.end(), isOdd);
  cout << "The first odd value is " << *it << endl;

can be written in Java as,

List<Integer> myList = Arrays.asList(10, 25, 40, 55);
List<Integer> oddNums = (List<Integer>) CollectionUtils.select(myList,
  new Predicate<Integer>() {
    public boolean apply(Integer i) {
      return i % 2 != 0;
    }
  }
);
System.out.println("The first odd value is "+oddNums.get(0));

Please note that, unlike in C++ example, this would create a new list of the elements satisfying the specified predicate.

EDIT :

As Matthew Flaschen has suggested in a comment below, CollectionUtils.find is even closer to what you need. So, with find, the above code can be rewritten as:

List<Integer> myList = Arrays.asList(10, 25, 40, 55);
Integer firstOdd = (Integer) CollectionUtils.find(myList,
  new Predicate<Integer>() {
    public boolean apply(Integer i) {
      return i % 2 == 1;
    }
  }
);
System.out.println("The first odd value is "+firstOdd);
missingfaktor
CollectionUtils.find (http://commons.apache.org/collections/apidocs/org/apache/commons/collections/CollectionUtils.html#find%28java.util.Collection,%20org.apache.commons.collections.Predicate%29) is even closer; it returns just the first matching element.
Matthew Flaschen
@Matthew: Thanks. Updated the answer to include that.
missingfaktor
Never use apache-collections unless you are already in java 1.4.2 or lower.
KitsuneYMG
@kts: Point noted. Still not a good enough reason to downvote IMO.
missingfaktor
Unrelated to anything important, note that your isOdd predicate is broken. :)
Kevin Bourrillion
@Kevin: Fixed it. Thanks. :) Btw I had picked the C++ example from here -> http://www.cplusplus.com/reference/algorithm/find_if/.
missingfaktor
A: 

By using lambdaj you can easily filter a java collection in a very readable way. For example the following statement:

select(persons, having(on(Person.class).getAge(), greaterThan(30)));

selects all the persons in your list having more than 30 years.

Mario Fusco
+1  A: 

The problem is that using a method like find_if should make the code simpler to write and easier to read. However, IMHO Java does not lend itself to functional notation and most of the time it is clearer and simpler to just write a natural loop. i.e. the code is shorter and doesn't require knowledge of libraries most people don't use. If this functionality was built in and Java supported Closures (as it appears Java 7 will) then using predicates and functional methods would make more sense.

One measure of complexity is to count the number of symbols (counting open/close brackets as one) Using this measure of complexity, most predicate based solutions have more symbols and are possibly more complex and difficult for developers to read/maintain.

In the example given by @Roman, there are 15 symbols. In the loop example, there are 10 symbols.

List<Car> premiumCars = new ArrayList();
for(Car car: cars)
   if(car.price > 50000)
      premiumCars.add(car);

In the example by @Mario Fuscom, there is 9 symbols, in the following example there is 9 symbols. However, no non-standard functions are required and anyone who knows Java can read/maintain it.

List peopleOver30 = new ArrayList();
for(Person person: people)
   if(person.age > 30)
      peopleOver30.add(person);

Taking the last example from @Rahul G - I hate Unicorns, there are 13 symbols. In the loop example, there are 8 symbols.

Integer firstOdd = null;
for(int i: myList) 
    if(i % 2 == 1) {
       firstOdd = i;
       break;
    } 

Functional programming may make more sense to you because that is your development background, but this doesn't mean it is the natural or simplest way to express this in Java. Java 7 may change this....

Peter Lawrey
Good response Peter. I've tried using a functional style within Java, and have also discovered it makes the code MORE complex, and MORE difficult to understand.
Steve McLeod