Java 中是否有类似于 find_if 的函数?

10
在C++中,我可以使用带有谓词的find_if函数在容器中查找元素。在Java中有类似的方法吗?集合上的contains方法使用equals并且不能被参数化。

谓词是我们想到的内容,可以查看http://commons.apache.org/collections/api-2.1.1/org/apache/commons/collections/class-use/Predicate.html。 - Narayan
1
另外,请查看http://code.google.com/p/google-collections,特别是谓词和函数接口。 - gpampara
5个回答

11

您可以使用Google Collections中的Predicate。这里是tutorial和其中的一个示例:

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

你还可以查看这个主题:最佳的集合过滤方式是什么?


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)将提供第一个匹配元素。 - Matthew Flaschen
@Matthew Flaschen:是的,如果我们只需要获取至少1个对应项,则Iterables.find将是最佳解决方案。 - Roman
我认为你正在使用一个非常旧的Google Collections预发布快照!如果你想将过滤后的可迭代对象复制到一个不可变列表中(而不仅仅是直接遍历它),请使用ImmutableList.copyOf()。 - Kevin Bourrillion
@Kevin Bourrillion:你可能是对的,但这只是教程中的一段代码。它可以作为一个很好的起点,而不是从这里复制并粘贴到你的程序中的代码。我还发现了当前Google Collections API和教程中使用的版本之间的一些差异。但所有的概念仍然是实际的。 - Roman

2
你可以使用Apache Commons中的CollectionUtils.select
例如,以下C++代码:
  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;

可以用Java编写为:
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));

请注意,与 C++ 示例不同的是,这将创建一个新的列表,其中包含满足指定谓词条件的元素。
编辑:
正如 Matthew Flaschen 在下面的评论中建议的那样,CollectionUtils.find 更接近你所需要的。因此,使用 find,上述代码可以重写为:
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);

CollectionUtils.find(http://commons.apache.org/collections/apidocs/org/apache/commons/collections/CollectionUtils.html#find%28java.util.Collection,%20org.apache.commons.collections.Predicate%29)更接近,它只返回第一个匹配的元素。 - Matthew Flaschen
@Matthew:谢谢。已更新答案,包含了那个内容。 - missingfaktor
除非你正在使用Java 1.4.2或更低版本,否则永远不要使用apache-collections。 - KitsuneYMG
@kts:注意到了。在我看来,这仍然不是一个足够好的理由来点踩。 - missingfaktor
1
与任何重要的事情无关,注意你的isOdd谓词是有问题的。 :) - Kevin Bourrillion
@Kevin:已经修复了。谢谢。:) 顺便说一下,我是从这里选取的C++示例-> http://www.cplusplus.com/reference/algorithm/find_if/。 - missingfaktor

1
问题在于使用像find_if这样的方法应该使代码编写更简单,阅读更容易。然而,在我看来,Java不适合函数符号表示法,大多数情况下,直接编写自然循环会更清晰、更简单。也就是说,代码更短,不需要了解大多数人不使用的库。如果这个功能被内置,并且Java支持闭包(似乎Java 7将支持),那么使用谓词和函数方法将更有意义。
一种复杂性的度量是计算符号的数量(将开/闭括号视为一个符号)。使用这种复杂性度量,大多数基于谓词的解决方案具有更多的符号,可能更复杂,难以供开发人员阅读/维护。
在@Roman给出的示例中,有15个符号。在循环示例中,有10个符号。
List<Car> premiumCars = new ArrayList();
for(Car car: cars)
   if(car.price > 50000)
      premiumCars.add(car);

在@Mario Fuscom的示例中,有9个符号,在下面的示例中也有9个符号。然而,不需要使用非标准函数,任何了解Java的人都可以阅读/维护它。
List peopleOver30 = new ArrayList();
for(Person person: people)
   if(person.age > 30)
      peopleOver30.add(person);

从@Rahul G - I hate Unicorns的最后一个例子中可以看出,有13个符号。在循环示例中,有8个符号。
Integer firstOdd = null;
for(int i: myList) 
    if(i % 2 == 1) {
       firstOdd = i;
       break;
    } 

函数式编程可能更符合您的开发背景,但这并不意味着它是在Java中表达这一点的自然或最简单的方式。Java 7 可能会改变这一点....


很好的回答,Peter。我尝试在Java中使用函数式风格,并发现它使代码更加复杂,更难理解。 - Steve McLeod

1
在Java 8中,我们有removeIf()方法,它可以根据特定谓词从集合中删除元素。但是我们没有find_if方法。不过,我们可以使用流API来实现它。
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

通过使用lambdaj,您可以以非常可读的方式轻松过滤Java集合。例如以下语句:

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

选择列表中所有年龄大于30岁的人。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接