如何过滤Java集合(基于谓词)?
问题的原因是需要根据谓词来过滤Java集合,但使用Apache Commons的CollectionUtils.filter方法时存在一些问题。该方法不是通用的,并且会修改原始集合。
解决方法是使用CollectionUtils中的其他过滤方法,这些方法不会修改原始集合。其中一个不会修改原始集合的方法是org.apache.commons.collections.CollectionUtils#select(Collection,Predicate)。在Commons Collections v4中,这个方法现在使用了泛型。
需要注意的是,使用这个方法时要小心,因为它依赖于iterator.remove()方法,而这个方法对于某些集合是可选的。因此,如果对数组等进行过滤操作,可能会出现UnsupportedOperationException。
解决方法的示例代码如下:
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.Predicate; ... CollectionfilteredCollection = CollectionUtils.select(collection, new Predicate () { @Override public boolean evaluate(T object) { // 这里根据谓词的条件来过滤集合 return object.satisfiesPredicate(); } });
通过使用这个解决方法,我们可以根据谓词来过滤Java集合,而不会修改原始集合,并且可以避免可能出现的异常情况。
如何根据谓词过滤Java集合?
在使用Java 1.5版本并且无法添加Google Collections的情况下,我会做类似于Google的方法。这是对Jon评论的轻微变化。
首先在代码库中添加以下接口。
public interface IPredicate{ boolean apply(T type); }
实现该接口的类可以回答某个类型是否满足某个谓词。例如,如果T是User类型,AuthorizedUserPredicate
然后在某个实用类中,你可以这样写:
public staticCollection filter(Collection target, IPredicate predicate) { Collection result = new ArrayList (); for (T element: target) { if (predicate.apply(element)) { result.add(element); } } return result; }
因此,假设你可以使用上面的方法,可以这样使用:
PredicateisAuthorized = new Predicate () { public boolean apply(User user) { // 绑定User中的一个布尔方法到一个引用 return user.isAuthorized(); } }; // allUsers是一个Collection Collection authorizedUsers = filter(allUsers, isAuthorized);
如果对线性检查的性能有所关注,那么可能希望有一个包含目标集合的领域对象。具有目标集合的领域对象将在初始化、添加和设置目标集合的方法中具有过滤逻辑。
更新:
在实用类(假设为Predicate)中,我添加了一个带有默认值选项的select方法,当谓词不返回预期值时使用,并添加了一个用于在新的IPredicate内部使用的静态属性。
public class Predicate { public static Object predicateParams; public staticCollection filter(Collection target, IPredicate predicate) { Collection result = new ArrayList (); for (T element : target) { if (predicate.apply(element)) { result.add(element); } } return result; } public static T select(Collection target, IPredicate predicate) { T result = null; for (T element : target) { if (!predicate.apply(element)) continue; result = element; break; } return result; } public static T select(Collection target, IPredicate predicate, T defaultValue) { T result = defaultValue; for (T element : target) { if (!predicate.apply(element)) continue; result = element; break; } return result; } }
以下示例查找两个集合之间缺失的对象:
ListmissingObjects = (List ) Predicate.filter(myCollectionOfA, new IPredicate () { public boolean apply(MyTypeA objectOfA) { Predicate.predicateParams = objectOfA.getName(); return Predicate.select(myCollectionB, new IPredicate () { public boolean apply(MyTypeB objectOfB) { return objectOfB.getName().equals(Predicate.predicateParams.toString()); } }) == null; } });
以下示例在一个集合中查找一个实例,并在找不到实例时返回集合的第一个元素作为默认值:
MyType myObject = Predicate.select(collectionOfMyType, new IPredicate() { public boolean apply(MyType objectOfMyType) { return objectOfMyType.isDefault(); }}, collectionOfMyType.get(0));
更新(Java 8发布后):
距离我(Alan)第一次发布这个答案已经过去了几年,我仍然无法相信我正在为这个答案获得SO点数。无论如何,现在Java 8已经将闭包引入语言中,我的答案现在会更简单。使用Java 8,不再需要一个独立的静态实用类。因此,如果要查找满足谓词的第一个元素。
final UserService userService = ... // 可能是注入的IoC final OptionaluserOption = userCollection.stream().filter(u -> { boolean isAuthorized = userService.isAuthorized(u); return isAuthorized; }).findFirst();
JDK 8的Optional API具有get()、isPresent()、orElse(defaultUser)、orElseGet(userSupplier)和orElseThrow(exceptionSupplier)等功能,以及其他“单子”函数,如map、flatMap和filter。
如果只想收集满足谓词的所有用户,则使用Collectors将流终止为所需的集合。
final UserService userService = ... // 可能是注入的IoC final ListuserOption = userCollection.stream().filter(u -> { boolean isAuthorized = userService.isAuthorized(u); return isAuthorized; }).collect(Collectors.toList());
更多关于Java 8流的示例,请参考这里。
是的,但我不想重复发明轮子。我宁愿找到一个能满足我的需求的实用库。
如果不需要新的集合,这不是最好的方法。使用过滤迭代器的比喻,可以将其输入到一个新的集合中,或者它可能是你所需要的全部。
在Scala的推导中,过滤将简单得多:`val authorized = for (user <- users if user.isAuthorized) yield user`
这会修改原始集合还是创建一个全新的集合?我试过使用这种方法,并记录下原始集合和从方法返回的集合,它们是相同的。
对于这个方法是否意味着修改原始集合有什么想法。我试过使用这个方法,它最终也过滤了我的原始集合。
这个方法不会修改原始集合。请注意,上面的结果集合是新构造的,filter方法只在谓词适用时向结果集合添加元素。
原文链接:https://stackoverflow.com/questions/1066589
Java 8引入了流和Lambda表达式来解决Java集合过滤的问题。通过使用流的filter方法和Lambda表达式,可以在一行代码中过滤集合,并将满足条件的元素收集到新的集合中。示例代码如下:
ListbeerDrinkers = persons.stream() .filter(p -> p.getAge() > 16) .collect(Collectors.toList());
除了使用流和Lambda表达式外,还可以使用`Collection#removeIf`方法来直接修改集合。下面的示例代码演示了如何使用`removeIf`方法来移除年龄小于等于16岁的人:
persons.removeIf(p -> p.getAge() <= 16);
另外,还可以使用第三方库lambdaj来过滤集合,而无需编写循环或内部类。示例代码如下:
ListbeerDrinkers = select(persons, having(on(Person.class).getAge(), greaterThan(16)));
然而,使用lambdaj可能会导致性能开销,并且在Android平台上可能不可用。此外,lambdaj的静态导入语句可能会让代码可读性下降。
除了上述方法外,自Java 8之后,还可以使用`Collection.removeIf`方法来过滤集合。这是最简单的方法之一。
总之,通过Java 8的流和Lambda表达式,以及`Collection.removeIf`方法和第三方库lambdaj,可以轻松地对Java集合进行过滤操作。这些方法可以提高代码的可读性和简洁性,使开发人员更加方便地进行集合过滤。