当前位置:  开发笔记 > 编程语言 > 正文

过滤Java Collection的最佳方法是什么?

如何解决《过滤JavaCollection的最佳方法是什么?》经验,为你挑选了15个好方法。

我想java.util.Collection根据谓词过滤一个.



1> Mario Fusco..:

Java 8 (2014) solves this problem using streams and lambdas in one line of code:

List 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):

persons.removeIf(p -> p.getAge() <= 16);

lambdaj allows filtering collections without writing loops or inner classes:

List beerDrinkers = select(persons, having(on(Person.class).getAge(),
    greaterThan(16)));

Can you imagine something more readable?

Disclaimer: I am a contributor on lambdaj


很好,但静态导入混淆了什么.作为参考,select/having/on是ch.lambdaj.Lambda上的静态导入,greaterThan是org.hamcrest.Matchers
LambdaJ非常性感,但值得注意的是它意味着显着的开销(平均2.6):https://code.google.com/p/lambdaj/wiki/PerformanceAnalysis.
显然无法在Android上运行:https://groups.google.com/forum/#!msg/lambdaj/km7uFgvSd3k/grJhgl3ik5sJ
真的很喜欢这个LamdaJ的例子......类似于.NET内置的Lambda函数.一个人16岁时可以在哪里喝酒?我们应该考虑添加本地化约束.:P
removeIf示例应该是`persons.removeIf(p - > p.getAge()<= 16);`
太糟糕了,它无法使用枚举.我切换到Apache Commons.

2> Alan..:

假设您使用的是Java 1.5,并且无法添加Google Collections,我会做一些与Google员工非常相似的事情.这是Jon的评论略有不同.

首先将此接口添加到您的代码库.

public interface IPredicate { boolean apply(T type); }

当某个谓词对某种类型为真时,它的实现者可以回答.例如,如果TUserAuthorizedUserPredicate实现IPredicate,则AuthorizedUserPredicate#apply返回是否User授权传入.

然后在一些实用课程中,你可以说

public static  Collection filter(Collection target, IPredicate predicate) {
    Collection result = new ArrayList();
    for (T element: target) {
        if (predicate.apply(element)) {
            result.add(element);
        }
    }
    return result;
}

所以,假设你已经使用了以上可能

Predicate isAuthorized = 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 static  Collection 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;
    }
}

以下示例查找集合之间缺少的对象:

List missingObjects = (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 Optional 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,flatMapfilter.

如果您只想收集与谓词匹配的所有用户,请使用Collectors终止所需集合中的流.

final UserService userService = ... // perhaps injected IoC
final List userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).collect(Collectors.toList());

有关Java 8流如何工作的更多示例,请参见此处.


是的,但我不想再次重复重新发明轮子.我宁愿找一些我想要的实用工具库.
如果您不想要新的集合,这不是最好的方法.使用过滤器迭代器隐喻,它可能会输入到新的集合中,或者它可能就是您需要的所有内容.

3> Kevin Wong..:

使用来自Apache Commons的CollectionUtils.filter(Collection,Predicate).


特别是,*不*修改集合的方法是org.apache.commons.collections.CollectionUtils #select(Collection,Predicate)
在Commons Collections v4中,现在使用泛型.
这没关系,但它不是通用的,并且修改了集合(不好)
CollectionUtils中还有其他过滤方法不会修改原始集合.

4> Vladimir Dyu..:

"最好的"方式是一个太宽的要求.它是"最短的"吗?"最快的"?"读"?过滤到另一个集合?

最简单(但不是最可读)的方法是迭代它并使用Iterator.remove()方法:

Iterator it = 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()以了解要保存在集合中的实例.

我真的没有理由为这项任务引入第三方库.


我的投票是针对这一点:它只是有效,没有外部库.我从未意识到实例化Iterator与使用for-each语法相比实际上是有用的,或者您可以从列表中删除没有ConcurrentModificationException或类似的东西.:)

5> Heath Border..:

考虑使用Google Collections来获取支持泛型的更新集合框架.

更新:谷歌馆藏库现已弃用.您应该使用最新版本的Guava.它仍然具有集合框架的所有相同扩展,包括基于谓词进行过滤的机制.


Kevin,Iterables.filter()和Iterators.filter()从一开始就在那里,通常都是你需要的.

6> gavenkoa..:

等待Java 8:

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


呃......真是太冗长了.他们为什么不能这样做:List result = personList.filter(p - > p.age> 30);
要直接在**Collection**上使用过滤器,你需要使用**removeIf**call:http://download.java.net/jdk8/docs/api/java/util/Collection.html#removeIf%28java.util. function.Predicate 29%
@KevinWong"详细"几乎描述了我想的整个语言.至少他们是一致的吗?
为什么不在最后一部分使用Collectors.toList()?
[这里](https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#removeIf-java.util.function.Predicate-)是一个链接gavenkoa提供,不404.`personList.removeIf(p - > p.age <30);`少详细.另外,我听说过开始实现apis,接受并返回`Stream`s而不是`Collection`s,因为`Stream`s非常有用而且速度很快,但往返于它们的速度很慢.

7> Josh M..:

从Java 8的早期版本开始,您可以尝试以下方法:

Collection collection = ...;
Stream stream = collection.stream().filter(...);

例如,如果您有一个整数列表,并且想要过滤大于10的数字然后将这些数字打印到控制台,您可以执行以下操作:

List numbers = Arrays.asList(12, 74, 5, 8, 16);
numbers.stream().filter(n -> n > 10).forEach(System.out::println);



8> anon..:

我会把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,可以发现在这里.



9> ykaganovich..:

您确定要过滤Collection本身而不是迭代器吗?

请参阅org.apache.commons.collections.iterators.FilterIterator

或者使用apache commons的版本4 org.apache.commons.collections4.iterators.FilterIterator



10> 小智..:

设置:

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

用法:

List myList = ...;
filterCollection(myList, new Predicate() {
  public boolean filter(MyObject obj) {
    return obj.shouldFilter();
  }
});


很好,但我更喜欢Alan实现,因为你得到了该集合的副本而不是改变它.此外,Alan的代码是线程安全的,而你的代码则不是.

11> Nestor Herna..:

一些简单而直接的Java怎么样?

 List list ...;
 List newList = new ArrayList<>();
 for (Customer c : list){
    if (c.getName().equals("dd")) newList.add(c);
 }

简单,易读且易于使用(适用于Android!)但是,如果您使用的是Java 8,则可以在一行中完成:

List newList = list.stream().filter(c -> c.getName().equals("dd")).collect(toList());

请注意,toList()是静态导入的



12> Donald Raab..:

让我们看一下如何使用Eclipse Collections(以前的GS Collections)过滤内置的JDK List和MutableList.

List jdkList = Arrays.asList(1, 2, 3, 4, 5);
MutableList ecList = Lists.mutable.with(1, 2, 3, 4, 5);

如果要过滤小于3的数字,您可能会得到以下输出.

List selected = 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的一些替代方法.

Predicate lessThan3 = 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.

PartitionIterable jdkPartitioned = 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的提交者.



13> Kevin Wong..:

Google的Guava库中的Collections2.filter(Collection,Predicate)方法可以满足您的需求.



14> akuhn..:

使用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 collection = ...

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



15> fbokovikov..:

由于启用了Java 9 Collectors.filtering

public static 
    Collector filtering(Predicate predicate,
                                 Collector downstream)

因此过滤应为:

collection.stream().collect(Collectors.filtering(predicate, collector))

例:

List oddNumbers = List.of(1, 19, 15, 10, -10).stream()
            .collect(Collectors.filtering(i -> i % 2 == 1, Collectors.toList()));

推荐阅读
刘美娥94662
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有