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

14 浏览
0 Comments

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

我想根据一个谓词来筛选一个 java.util.Collection

0
0 Comments

问题的原因是需要根据谓词来过滤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;
...
Collection filteredCollection = CollectionUtils.select(collection, new Predicate() {
    @Override
    public boolean evaluate(T object) {
        // 这里根据谓词的条件来过滤集合
        return object.satisfiesPredicate();
    }
});

通过使用这个解决方法,我们可以根据谓词来过滤Java集合,而不会修改原始集合,并且可以避免可能出现的异常情况。

0
0 Comments

如何根据谓词过滤Java集合?

在使用Java 1.5版本并且无法添加Google Collections的情况下,我会做类似于Google的方法。这是对Jon评论的轻微变化。

首先在代码库中添加以下接口。

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

实现该接口的类可以回答某个类型是否满足某个谓词。例如,如果T是User类型,AuthorizedUserPredicate实现了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) {
        // 绑定User中的一个布尔方法到一个引用
        return user.isAuthorized();
    }
};
// allUsers是一个Collection
Collection authorizedUsers = filter(allUsers, isAuthorized);

如果对线性检查的性能有所关注,那么可能希望有一个包含目标集合的领域对象。具有目标集合的领域对象将在初始化、添加和设置目标集合的方法中具有过滤逻辑。

更新:

在实用类(假设为Predicate)中,我添加了一个带有默认值选项的select方法,当谓词不返回预期值时使用,并添加了一个用于在新的IPredicate内部使用的静态属性。

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发布后):

距离我(Alan)第一次发布这个答案已经过去了几年,我仍然无法相信我正在为这个答案获得SO点数。无论如何,现在Java 8已经将闭包引入语言中,我的答案现在会更简单。使用Java 8,不再需要一个独立的静态实用类。因此,如果要查找满足谓词的第一个元素。

final UserService userService = ... // 可能是注入的IoC
final Optional userOption = 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 List userOption = 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

0
0 Comments

Java 8引入了流和Lambda表达式来解决Java集合过滤的问题。通过使用流的filter方法和Lambda表达式,可以在一行代码中过滤集合,并将满足条件的元素收集到新的集合中。示例代码如下:

List beerDrinkers = persons.stream()
    .filter(p -> p.getAge() > 16)
    .collect(Collectors.toList());

除了使用流和Lambda表达式外,还可以使用`Collection#removeIf`方法来直接修改集合。下面的示例代码演示了如何使用`removeIf`方法来移除年龄小于等于16岁的人:

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

另外,还可以使用第三方库lambdaj来过滤集合,而无需编写循环或内部类。示例代码如下:

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

然而,使用lambdaj可能会导致性能开销,并且在Android平台上可能不可用。此外,lambdaj的静态导入语句可能会让代码可读性下降。

除了上述方法外,自Java 8之后,还可以使用`Collection.removeIf`方法来过滤集合。这是最简单的方法之一。

总之,通过Java 8的流和Lambda表达式,以及`Collection.removeIf`方法和第三方库lambdaj,可以轻松地对Java集合进行过滤操作。这些方法可以提高代码的可读性和简洁性,使开发人员更加方便地进行集合过滤。

0