我想java.util.Collection
根据谓词过滤一个.
Java 8 (2014) solves this problem using streams and lambdas in one line of code:
ListbeerDrinkers = persons.stream() .filter(p -> p.getAge() > 16).collect(Collectors.toList());
Here's a tutorial.
Use Collection#removeIf
to modify the collection in place. (Notice: In this case, the predicate will remove objects who satisfy the predicate):
persons.removeIf(p -> p.getAge() <= 16);
lambdaj allows filtering collections without writing loops or inner classes:
ListbeerDrinkers = select(persons, having(on(Person.class).getAge(), greaterThan(16)));
Can you imagine something more readable?
Disclaimer: I am a contributor on lambdaj
假设您使用的是Java 1.5,并且无法添加Google Collections,我会做一些与Google员工非常相似的事情.这是Jon的评论略有不同.
首先将此接口添加到您的代码库.
public interface IPredicate{ boolean apply(T type); }
当某个谓词对某种类型为真时,它的实现者可以回答.例如,如果T
是User
和AuthorizedUserPredicate
实现IPredicate
,则AuthorizedUserPredicate#apply
返回是否User
授权传入.
然后在一些实用课程中,你可以说
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) { // binds a boolean method in User to a reference return user.isAuthorized(); } }; // allUsers is a Collection Collection authorizedUsers = filter(allUsers, isAuthorized);
如果关注线性检查的性能,那么我可能希望拥有一个具有目标集合的域对象.具有目标集合的域对象将具有用于初始化,添加和设置目标集合的方法的过滤逻辑.
更新:
在实用程序类中(比如说Predicate),我在谓词没有返回预期值时添加了一个带有默认值选项的select方法,还添加了一个在新IPredicate中使用的params的静态属性.
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发布之后):
我(艾伦)第一次发布这个答案已经好几年了,我仍然无法相信我正在为这个答案收集SO点数.无论如何,现在Java 8已经引入了该语言的闭包,我的答案现在会有很大的不同,而且更简单.使用Java 8,不需要一个独特的静态实用程序类.所以如果你想找到与你的谓词匹配的第一个元素.
final UserService userService = ... // perhaps injected IoC final OptionaluserOption = 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 ListuserOption = userCollection.stream().filter(u -> { boolean isAuthorized = userService.isAuthorized(u); return isAuthorized; }).collect(Collectors.toList());
有关Java 8流如何工作的更多示例,请参见此处.
使用来自Apache Commons的CollectionUtils.filter(Collection,Predicate).
"最好的"方式是一个太宽的要求.它是"最短的"吗?"最快的"?"读"?过滤到另一个集合?
最简单(但不是最可读)的方法是迭代它并使用Iterator.remove()方法:
Iteratorit = col.iterator(); while( it.hasNext() ) { Foo foo = it.next(); if( !condition(foo) ) it.remove(); }
现在,为了使其更具可读性,您可以将其包装到实用程序方法中.然后发明一个IPredicate接口,创建该接口的匿名实现,并执行以下操作:
CollectionUtils.filterInPlace(col, new IPredicate(){ public boolean keepIt(Foo foo) { return foo.isBar(); } });
其中filterInPlace()迭代集合并调用Predicate.keepIt()以了解要保存在集合中的实例.
我真的没有理由为这项任务引入第三方库.
考虑使用Google Collections来获取支持泛型的更新集合框架.
更新:谷歌馆藏库现已弃用.您应该使用最新版本的Guava.它仍然具有集合框架的所有相同扩展,包括基于谓词进行过滤的机制.
等待Java 8:
ListolderThan30 = //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());
从Java 8的早期版本开始,您可以尝试以下方法:
Collectioncollection = ...; Stream stream = collection.stream().filter(...);
例如,如果您有一个整数列表,并且想要过滤大于10的数字然后将这些数字打印到控制台,您可以执行以下操作:
Listnumbers = 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() { public Boolean call(Integer i) { return i % 2 != 0; } }) .subscribe(new Action1 () { public void call(Integer i) { System.out.println(i); } });
输出:
1 3 5
在RxJava的更多细节filter
,可以发现在这里.
您确定要过滤Collection本身而不是迭代器吗?
请参阅org.apache.commons.collections.iterators.FilterIterator
或者使用apache commons的版本4 org.apache.commons.collections4.iterators.FilterIterator
设置:
public interface Predicate{ public boolean filter(T t); } void filterCollection(Collection col, Predicate predicate) { for (Iterator i = col.iterator(); i.hasNext();) { T obj = i.next(); if (predicate.filter(obj)) { i.remove(); } } }
用法:
ListmyList = ...; filterCollection(myList, new Predicate () { public boolean filter(MyObject obj) { return obj.shouldFilter(); } });
一些简单而直接的Java怎么样?
Listlist ...; List newList = new ArrayList<>(); for (Customer c : list){ if (c.getName().equals("dd")) newList.add(c); }
简单,易读且易于使用(适用于Android!)但是,如果您使用的是Java 8,则可以在一行中完成:
ListnewList = list.stream().filter(c -> c.getName().equals("dd")).collect(toList());
请注意,toList()是静态导入的
让我们看一下如何使用Eclipse Collections(以前的GS Collections)过滤内置的JDK List和MutableList.
ListjdkList = Arrays.asList(1, 2, 3, 4, 5); MutableList ecList = Lists.mutable.with(1, 2, 3, 4, 5);
如果要过滤小于3的数字,您可能会得到以下输出.
Listselected = Lists.mutable.with(1, 2); List rejected = Lists.mutable.with(3, 4, 5);
以下是使用匿名内部类进行过滤的方法Predicate
.
Assert.assertEquals(selected, Iterate.select(jdkList, each -> each < 3)); Assert.assertEquals(rejected, Iterate.reject(jdkList, each -> each < 3)); Assert.assertEquals(selected, ecList.select(each -> each < 3)); Assert.assertEquals(rejected, ecList.reject(each -> each < 3));
以下是使用Predicates工厂过滤JDK列表和Eclipse Collections MutableLists的一些替代方法.
PredicatelessThan3 = new Predicate () { public boolean accept(Integer each) { return each < 3; } }; Assert.assertEquals(selected, Iterate.select(jdkList, lessThan3)); Assert.assertEquals(selected, ecList.select(lessThan3));
这是一个不为谓词分配对象的版本,通过使用Predicates2工厂而不是Predicate
采用a 的方法selectWith
.
Assert.assertEquals(selected, Iterate.select(jdkList, Predicates.lessThan(3))); Assert.assertEquals(selected, ecList.select(Predicates.lessThan(3)));
有时您想要在负面条件下进行过滤.Eclipse Collections中有一个特殊的方法用于调用它Predicate2
.
Assert.assertEquals( selected, ecList.selectWith(Predicates2.lessThan(), 3));
以下是使用Java 8 lambda进行过滤的方法reject
.
Assert.assertEquals(rejected, Iterate.reject(jdkList, lessThan3)); Assert.assertEquals(rejected, ecList.reject(lessThan3));
该方法partition
将返回两个集合,包含由其选择和拒绝的元素Predicate
.
PartitionIterablejdkPartitioned = Iterate.partition(jdkList, lessThan3); Assert.assertEquals(selected, jdkPartitioned.getSelected()); Assert.assertEquals(rejected, jdkPartitioned.getRejected()); PartitionList ecPartitioned = gscList.partition(lessThan3); Assert.assertEquals(selected, ecPartitioned.getSelected()); Assert.assertEquals(rejected, ecPartitioned.getRejected());
注意:我是Eclipse Collections的提交者.
Google的Guava库中的Collections2.filter(Collection,Predicate)方法可以满足您的需求.
使用ForEach DSL,您可以写
import static ch.akuhn.util.query.Query.select; import static ch.akuhn.util.query.Query.$result; import ch.akuhn.util.query.Select; Collectioncollection = ... for (Select each : select(collection)) { each.yield = each.value.length() > 3; } Collection result = $result();
给出[The,quick,brown,fox,jumps,over,the,lazy,dog]的集合,这会导致[quick,brown,jumps,over,lazy],即所有字符串超过三个字符.
ForEach DSL支持的所有迭代样式都是
AllSatisfy
AnySatisfy
Collect
Counnt
CutPieces
Detect
GroupedBy
IndexOf
InjectInto
Reject
Select
有关详细信息,请参阅https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach
由于启用了Java 9 Collectors.filtering
:
public staticCollector filtering(Predicate super T> predicate, Collector super T, A, R> downstream)
因此过滤应为:
collection.stream().collect(Collectors.filtering(predicate, collector))
例:
ListoddNumbers = List.of(1, 19, 15, 10, -10).stream() .collect(Collectors.filtering(i -> i % 2 == 1, Collectors.toList()));