我希望基于谓词对java.util.Collection
进行筛选。
Java 8(2014)使用流和Lambda仅需一行代码即可解决此问题:
List<Person> beerDrinkers = persons.stream()
.filter(p -> p.getAge() > 16).collect(Collectors.toList());
这是一个教程。
使用Collection#removeIf
直接修改集合。 (注意:在这种情况下,谓词将删除满足谓词条件的对象):
persons.removeIf(p -> p.getAge() <= 16);
lambdaj可以让您在不编写循环或内部类的情况下过滤集合:
List<Person> beerDrinkers = select(persons, having(on(Person.class).getAge(),
greaterThan(16)));
你能想象出比这更易读的东西吗?
免责声明:我是lambdaj的贡献者。
public interface IPredicate<T> { boolean apply(T type); }
实现者可以在特定类型满足某个谓词时进行回答。例如,如果 T
是 User
,AuthorizedUserPredicate<User>
实现了 IPredicate<T>
,那么 AuthorizedUserPredicate#apply
返回传入的 User
是否被授权。
然后,在某个实用类中,您可以这样说:
public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
Collection<T> result = new ArrayList<T>();
for (T element: target) {
if (predicate.apply(element)) {
result.add(element);
}
}
return result;
}
假设您可以使用上述内容
Predicate<User> isAuthorized = new Predicate<User>() {
public boolean apply(User user) {
// binds a boolean method in User to a reference
return user.isAuthorized();
}
};
// allUsers is a Collection<User>
Collection<User> authorizedUsers = filter(allUsers, isAuthorized);
public class Predicate {
public static Object predicateParams;
public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
Collection<T> result = new ArrayList<T>();
for (T element : target) {
if (predicate.apply(element)) {
result.add(element);
}
}
return result;
}
public static <T> T select(Collection<T> target, IPredicate<T> predicate) {
T result = null;
for (T element : target) {
if (!predicate.apply(element))
continue;
result = element;
break;
}
return result;
}
public static <T> T select(Collection<T> target, IPredicate<T> predicate, T defaultValue) {
T result = defaultValue;
for (T element : target) {
if (!predicate.apply(element))
continue;
result = element;
break;
}
return result;
}
}
List<MyTypeA> missingObjects = (List<MyTypeA>) Predicate.filter(myCollectionOfA,
new IPredicate<MyTypeA>() {
public boolean apply(MyTypeA objectOfA) {
Predicate.predicateParams = objectOfA.getName();
return Predicate.select(myCollectionB, new IPredicate<MyTypeB>() {
public boolean apply(MyTypeB objectOfB) {
return objectOfB.getName().equals(Predicate.predicateParams.toString());
}
}) == null;
}
});
MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));
Java 8发布后更新:
距离我(Alan)第一次发布此答案已经过去了几年,我仍然无法相信我正在为此答案收集SO积分。无论如何,现在Java 8已经将闭包引入语言中,我的答案现在会更加简单明了。使用Java 8,不需要一个独立的静态实用程序类。因此,如果您想要找到与谓词匹配的第一个元素。
final UserService userService = ... // perhaps injected IoC
final Optional<UserModel> userOption = userCollection.stream().filter(u -> {
boolean isAuthorized = userService.isAuthorized(u);
return isAuthorized;
}).findFirst();
JDK 8针对可选项的API具有get()
、isPresent()
、orElse(defaultUser)
、orElseGet(userSupplier)
和orElseThrow(exceptionSupplier)
的能力,以及其他“单子”功能,如map
、flatMap
和filter
。
如果您只想收集与谓词匹配的所有用户,则使用Collectors
将流终止在所需的集合中。
final UserService userService = ... // perhaps injected IoC
final List<UserModel> userOption = userCollection.stream().filter(u -> {
boolean isAuthorized = userService.isAuthorized(u);
return isAuthorized;
}).collect(Collectors.toList());
请点击这里查看更多关于Java 8流的示例。
val authorized = for (user <- users if user.isAuthorized) yield user
。 - AlanIterator<Foo> it = col.iterator();
while( it.hasNext() ) {
Foo foo = it.next();
if( !condition(foo) ) it.remove();
}
现在,为了使其更易读,您可以将其包装到一个实用方法中。然后发明一个IPredicate接口,创建该接口的匿名实现,并执行以下操作:
CollectionUtils.filterInPlace(col,
new IPredicate<Foo>(){
public boolean keepIt(Foo foo) {
return foo.isBar();
}
});
在 filterInPlace() 中,迭代集合并调用 Predicate.keepIt() 方法来确定是否保留集合中的实例。
我真的不认为为了这个任务而引入第三方库是有道理的。
等待Java 8的到来:
List<Person> olderThan30 =
//Create a Stream from the personList
personList.stream().
//filter the element to select only those with age >= 30
filter(p -> p.age >= 30).
//put those filtered elements into a new List.
collect(Collectors.toList());
personList.removeIf(p -> p.age < 30);
简洁些。另外,我听说开始实现接受和返回 Stream
而不是 Collection
的 API,因为 Stream
非常有用且快速,但是转换它们的速度很慢。 - Captain Man自Java 8早期版本发布以来,您可以尝试类似以下的操作:
Collection<T> collection = ...;
Stream<T> stream = collection.stream().filter(...);
例如,如果您有一个整数列表,并且想要过滤掉大于10的数字,然后将这些数字打印到控制台上,您可以像这样做:List<Integer> numbers = Arrays.asList(12, 74, 5, 8, 16);
numbers.stream().filter(n -> n > 10).forEach(System.out::println);
我会提出 RxJava,它也可在Android上使用。 如果您希望在集合中添加更多转换或处理过滤时出现的错误,RxJava可能并不总是最佳选择,但它会给您带来更多的灵活性。
Observable.from(Arrays.asList(1, 2, 3, 4, 5))
.filter(new Func1<Integer, Boolean>() {
public Boolean call(Integer i) {
return i % 2 != 0;
}
})
.subscribe(new Action1<Integer>() {
public void call(Integer i) {
System.out.println(i);
}
});
输出:
1
3
5
有关RxJava的filter
的更多详细信息可以在此处找到。
Collectors.filtering
:public static <T, A, R>
Collector<T, ?, R> filtering(Predicate<? super T> predicate,
Collector<? super T, A, R> downstream)
collection.stream().collect(Collectors.filtering(predicate, collector))
例子:
List<Integer> oddNumbers = List.of(1, 19, 15, 10, -10).stream()
.collect(Collectors.filtering(i -> i % 2 == 1, Collectors.toList()));
您确定要过滤集合本身,而不是迭代器吗?
请参见org.apache.commons.collections.iterators.FilterIterator
或者使用Apache Commons的第4版org.apache.commons.collections4.iterators.FilterIterator
persons.removeIf(p -> p.getAge() <= 16);
。 - vim