10

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.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • Predicate is what comes to mind chk http://commons.apache.org/collections/api-2.1.1/org/apache/commons/collections/class-use/Predicate.html – Narayan Apr 05 '10 at 09:30
  • 1
    Also, have a look at http://code.google.com/p/google-collections, especially the predicate and function interfaces. – gpampara Apr 05 '10 at 09:44

5 Answers5

11

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

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

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

Community
  • 1
  • 1
Roman
  • 64,384
  • 92
  • 238
  • 332
  • `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 Apr 05 '10 at 11:35
  • @Matthew Flaschen: yes, if we only need to get at least 1 correspondent item then `Iterables.find` would be the best solution. – Roman Apr 05 '10 at 12:02
  • 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 Apr 05 '10 at 22:57
  • @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 Apr 05 '10 at 23:45
2

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
  • 90,905
  • 62
  • 285
  • 365
1

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

Steve McLeod
  • 51,737
  • 47
  • 128
  • 184
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 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 Apr 10 '10 at 15:15
1

In Java 8, we have removeIf() which removes elements from the collections with certain Predicate. But we do not have something like find_if. But we can use stream API to achieve this.

List<Integer> list = Arrays.asList(20,35,50,654);
int result = list.stream().filter(i-> i%2!=0).findFirst().orElse(0).intValue();
System.out.println(result);
0

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
  • 13,548
  • 3
  • 28
  • 37