过滤 Java 集合的最佳方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/122105/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the best way to filter a Java Collection?
提问by Kevin Wong
I want to filter a java.util.Collection
based on a predicate.
我想java.util.Collection
根据谓词过滤 a 。
采纳答案by Mario Fusco
Java 8 (2014) solves this problem using streams and lambdas in one line of code:
Java 8 ( 2014) 在一行代码中使用流和 lambda 来解决这个问题:
List<Person> beerDrinkers = 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):
用于Collection#removeIf
就地修改集合。(注意:在这种情况下,谓词将删除满足谓词的对象):
persons.removeIf(p -> p.getAge() <= 16);
lambdajallows filtering collections without writing loops or inner classes:
lambdaj允许过滤集合而无需编写循环或内部类:
List<Person> beerDrinkers = select(persons, having(on(Person.class).getAge(),
greaterThan(16)));
Can you imagine something more readable?
你能想象出更具可读性的东西吗?
Disclaimer:I am a contributor on lambdaj
免责声明:我是 lambdaj 的贡献者
回答by Kevin Wong
Use CollectionUtils.filter(Collection,Predicate), from Apache Commons.
使用来自 Apache Commons 的CollectionUtils.filter(Collection,Predicate)。
回答by Heath Borders
Consider Google Collectionsfor an updated Collections framework that supports generics.
考虑使用Google Collections获取支持泛型的更新集合框架。
UPDATE: The google collections library is now deprecated. You should use the latest release of Guavainstead. It still has all the same extensions to the collections framework including a mechanism for filtering based on a predicate.
更新:谷歌收藏库现已弃用。您应该改用最新版本的Guava。它仍然具有对集合框架的所有相同扩展,包括基于谓词的过滤机制。
回答by ykaganovich
Are you sure you want to filter the Collection itself, rather than an iterator?
您确定要过滤 Collection 本身而不是迭代器吗?
see org.apache.commons.collections.iterators.FilterIterator
参见org.apache.commons.collections.iterators.FilterIterator
or using version 4 of apache commons org.apache.commons.collections4.iterators.FilterIterator
或使用 apache commons org.apache.commons.collections4.iterators.FilterIterator 的第 4 版
回答by jon
The setup:
设置:
public interface Predicate<T> {
public boolean filter(T t);
}
void filterCollection(Collection<T> col, Predicate<T> predicate) {
for (Iterator i = col.iterator(); i.hasNext();) {
T obj = i.next();
if (predicate.filter(obj)) {
i.remove();
}
}
}
The usage:
用法:
List<MyObject> myList = ...;
filterCollection(myList, new Predicate<MyObject>() {
public boolean filter(MyObject obj) {
return obj.shouldFilter();
}
});
回答by Vladimir Dyuzhev
"Best" way is too wide a request. Is it "shortest"? "Fastest"? "Readable"? Filter in place or into another collection?
“最佳”方式的要求太宽泛了。是“最短”吗?“最快的”?“可读”?就地过滤还是过滤到另一个集合?
Simplest (but not most readable) way is to iterate it and use Iterator.remove() method:
最简单(但不是最易读)的方法是迭代它并使用 Iterator.remove() 方法:
Iterator<Foo> it = col.iterator();
while( it.hasNext() ) {
Foo foo = it.next();
if( !condition(foo) ) it.remove();
}
Now, to make it more readable, you can wrap it into a utility method. Then invent a IPredicate interface, create an anonymous implementation of that interface and do something like:
现在,为了使其更具可读性,您可以将其包装到一个实用程序方法中。然后发明一个 IPredicate 接口,创建该接口的匿名实现并执行以下操作:
CollectionUtils.filterInPlace(col,
new IPredicate<Foo>(){
public boolean keepIt(Foo foo) {
return foo.isBar();
}
});
where filterInPlace() iterate the collection and calls Predicate.keepIt() to learn if the instance to be kept in the collection.
其中 filterInPlace() 迭代集合并调用 Predicate.keepIt() 以了解实例是否要保留在集合中。
I don't really see a justification for bringing in a third-party library just for this task.
我真的没有看到为这项任务引入第三方库的理由。
回答by Alan
Assuming that you are using Java 1.5, and that you cannot add Google Collections, I would do something very similar to what the Google guys did. This is a slight variation on Jon's comments.
假设您使用的是Java 1.5并且您无法添加Google Collections,我会做一些与 Google 人员所做的非常相似的事情。这与乔恩的评论略有不同。
First add this interface to your codebase.
首先将此接口添加到您的代码库中。
public interface IPredicate<T> { boolean apply(T type); }
Its implementers can answer when a certain predicate is true of a certain type. E.g. If T
were User
and AuthorizedUserPredicate<User>
implements IPredicate<T>
, then AuthorizedUserPredicate#apply
returns whether the passed in User
is authorized.
当某个谓词对某种类型为真时,它的实现者可以回答。例如,如果T
是User
和AuthorizedUserPredicate<User>
实现IPredicate<T>
,则AuthorizedUserPredicate#apply
返回传入的是否User
被授权。
Then in some utility class, you could say
然后在一些实用程序类中,你可以说
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;
}
So, assuming that you have the use of the above might be
所以,假设你有使用上面的可能是
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);
If performance on the linear check is of concern, then I might want to have a domain object that has the target collection. The domain object that has the target collection would have filtering logic for the methods that initialize, add and set the target collection.
如果关注线性检查的性能,那么我可能想要一个具有目标集合的域对象。具有目标集合的域对象将具有用于初始化、添加和设置目标集合的方法的过滤逻辑。
UPDATE:
更新:
In the utility class (let's say Predicate), I have added a select method with an option for default value when the predicate doesn't return the expected value, and also a static property for params to be used inside the new IPredicate.
在实用程序类(假设谓词)中,我添加了一个 select 方法,当谓词不返回预期值时,我添加了一个默认值选项,还添加了一个静态属性,用于在新的 IPredicate 中使用的 params。
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;
}
}
The following example looks for missing objects between collections:
以下示例在集合之间查找丢失的对象:
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;
}
});
The following example, looks for an instance in a collection, and returns the first element of the collection as default value when the instance is not found:
以下示例在集合中查找实例,并在未找到该实例时返回集合的第一个元素作为默认值:
MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));
UPDATE (after Java 8 release):
更新(Java 8 发布后):
It's been several years since I (Alan) first posted this answer, and I still cannot believe I am collecting SO points for this answer. At any rate, now that Java 8 has introduced closures to the language, my answer would now be considerably different, and simpler. With Java 8, there is no need for a distinct static utility class. So if you want to find the 1st element that matches your predicate.
自从我(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();
The JDK 8 API for optionals has the ability to get()
, isPresent()
, orElse(defaultUser)
, orElseGet(userSupplier)
and orElseThrow(exceptionSupplier)
, as well as other 'monadic' functions such as map
, flatMap
and filter
.
JDK 8 API for optionals 具有get()
, isPresent()
, orElse(defaultUser)
, orElseGet(userSupplier)
andorElseThrow(exceptionSupplier)
以及其他“monadic”功能,例如map
,flatMap
和filter
。
If you want to simply collect all the users which match the predicate, then use the Collectors
to terminate the stream in the desired collection.
如果您只想收集与谓词匹配的所有用户,请使用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());
See herefor more examples on how Java 8 streams work.
有关Java 8 流如何工作的更多示例,请参见此处。
回答by Kevin Wong
The Collections2.filter(Collection,Predicate)method in Google's Guava librarydoes just what you're looking for.
Google 的 Guava 库中的Collections2.filter(Collection,Predicate)方法可以满足您的需求。
回答by akuhn
With the ForEach DSL you may write
使用 ForEach DSL,您可以编写
import static ch.akuhn.util.query.Query.select;
import static ch.akuhn.util.query.Query.$result;
import ch.akuhn.util.query.Select;
Collection<String> collection = ...
for (Select<String> each : select(collection)) {
each.yield = each.value.length() > 3;
}
Collection<String> result = $result();
Given a collection of [The, quick, brown, fox, jumps, over, the, lazy, dog] this results in [quick, brown, jumps, over, lazy], ie all strings longer than three characters.
给定一组 [The, quick, brown, fox, jumps, over, the, lazy, dog] 这会导致 [quick, brown, jumps, over, lazy],即所有长度超过三个字符的字符串。
All iteration styles supported by the ForEach DSL are
ForEach DSL 支持的所有迭代样式都是
AllSatisfy
AnySatisfy
Collect
Counnt
CutPieces
Detect
GroupedBy
IndexOf
InjectInto
Reject
Select
AllSatisfy
AnySatisfy
Collect
Counnt
CutPieces
Detect
GroupedBy
IndexOf
InjectInto
Reject
Select
For more details, please refer to https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach
更多详情请参考https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach
回答by jdc0589
This, combined with the lack of real closures, is my biggest gripe for Java. Honestly, most of the methods mentioned above are pretty easy to read and REALLY efficient; however, after spending time with .Net, Erlang, etc... list comprehension integrated at the language level makes everything so much cleaner. Without additions at the language level, Java just cant be as clean as many other languages in this area.
这一点,再加上缺乏真正的闭包,是我对 Java 最大的抱怨。老实说,上面提到的大多数方法都非常容易阅读并且非常有效;然而,在花时间使用 .Net、Erlang 等之后……在语言级别集成的列表理解使一切变得更加清晰。如果没有语言级别的添加,Java 就不能像该领域的许多其他语言一样干净。
If performance is a huge concern, Google collections is the way to go (or write your own simple predicate utility). Lambdaj syntax is more readable for some people, but it is not quite as efficient.
如果性能是一个巨大的问题,谷歌集合是要走的路(或编写自己的简单谓词实用程序)。Lambdaj 语法对某些人来说更易读,但效率不高。
And then there is a library I wrote. I will ignore any questions in regard to its efficiency (yea, its that bad)...... Yes, i know its clearly reflection based, and no I don't actually use it, but it does work:
然后有一个我写的图书馆。我将忽略关于它的效率的任何问题(是的,它很糟糕)......是的,我知道它清楚地基于反射,不,我实际上并没有使用它,但它确实有效:
LinkedList<Person> list = ......
LinkedList<Person> filtered =
Query.from(list).where(Condition.ensure("age", Op.GTE, 21));
OR
或者
LinkedList<Person> list = ....
LinkedList<Person> filtered = Query.from(list).where("x => x.age >= 21");