首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何过滤Java集合(基于谓词)?

如何过滤Java集合(基于谓词)?
EN

Stack Overflow用户
提问于 2008-09-23 16:26:26
回答 21查看 737.2K关注 0票数 732

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

EN

回答 21

Stack Overflow用户

发布于 2008-09-23 16:41:41

假设你正在使用Java 1.5,并且你不能添加Google Collections,我会做一些非常类似于谷歌人所做的事情。这是Jon评论的一个细微的变化。

首先,将此接口添加到代码库中。

代码语言:javascript
复制
public interface IPredicate<T> { boolean apply(T type); }

它的实现者可以回答当某个谓词对某个类型为真时。例如,如果TUser,并且AuthorizedUserPredicate<User>实现了IPredicate<T>,则AuthorizedUserPredicate#apply将返回传入的User是否经过授权。

然后在某个实用程序类中,您可以这样说

代码语言:javascript
复制
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;
}

因此,假设您已经使用了上面的代码,则可能是

代码语言:javascript
复制
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);

如果线性检查的性能令人担忧,那么我可能希望拥有一个具有目标集合的域对象。具有目标集合的域对象将具有用于初始化、添加和设置目标集合的方法的过滤逻辑。

更新:

在实用程序类(假设是Predicate)中,我添加了一个select方法,该方法在谓词未返回预期值时具有默认值选项,还添加了一个用于在新IPredicate中使用的参数的静态属性。

代码语言:javascript
复制
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;
    }
}

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

代码语言:javascript
复制
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;
        }
    });

下面的示例在集合中查找实例,并在找不到该实例时返回集合的第一个元素作为默认值:

代码语言:javascript
复制
MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
    return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));

更新(在Java 8发布之后):

自从我(Alan)第一次发布这个答案已经有好几年了,我仍然不敢相信我为这个答案收集了这么多积分。无论如何,既然Java 8已经在该语言中引入了闭包,我的回答将会有很大的不同,并且更加简单。在Java 8中,不需要不同的静态实用程序类。所以如果你想找到与你的谓词匹配的第一个元素。

代码语言:javascript
复制
final UserService userService = ... // perhaps injected IoC
final Optional<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).findFirst();

用于可选选项的JDK8API具有get()isPresent()orElse(defaultUser)orElseGet(userSupplier)orElseThrow(exceptionSupplier)的能力,以及其他“一元”函数,如mapflatMapfilter

如果您想简单地收集与谓词匹配的所有用户,那么可以使用Collectors来终止所需集合中的流。

代码语言:javascript
复制
final UserService userService = ... // perhaps injected IoC
final List<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).collect(Collectors.toList());

有关Java8 streams如何工作的更多示例,请参阅here

票数 228
EN

Stack Overflow用户

发布于 2008-09-23 16:28:44

票数 93
EN

Stack Overflow用户

发布于 2008-09-23 16:41:33

“最好的”方式太宽泛了。它是“最短的”吗?“最快”?“可读性”?原地过滤还是放到另一个集合中?

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

代码语言:javascript
复制
Iterator<Foo> it = col.iterator();
while( it.hasNext() ) {
  Foo foo = it.next();
  if( !condition(foo) ) it.remove();
}

现在,为了使其更具可读性,您可以将其包装到一个实用方法中。然后发明一个IPredicate接口,创建该接口的匿名实现,并执行以下操作:

代码语言:javascript
复制
CollectionUtils.filterInPlace(col,
  new IPredicate<Foo>(){
    public boolean keepIt(Foo foo) {
      return foo.isBar();
    }
  });

其中filterInPlace()迭代集合并调用Predicate.keepIt()来了解实例是否要保留在集合中。

我真的看不到只为这项任务引入第三方库的理由。

票数 68
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/122105

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档