This article is participating in the Java Theme Month – Java Debug Notes EventActive link

Question: What is the best way to filter Java collections?

I want to filter through Predicate java.util.Collection.

Answer 1 (from a Lamdaj contributor) :

Java 8(2014) solved this problem with a single line of code by introducing the java.util.stream package and Lambda expressions:

List<Person> beerDrinkers = persons.stream()
    .filter(p -> p.getAge() > 16).collect(Collectors.toList());
Copy the code

Use collection.removeIf to modify the Collection if the conditions are met. (Note: in this case, objects that meet the criteria will be deleted) :

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

The Lambdaj library allows for filtering collections without writing loops or inner classes (Lambdaj allows for pseudo-function and statically typed manipulation of collections, partially removing the burden of writing loops (often nested and poorly readable) when iterating over collections) :

List<Person> beerDrinkers = select(persons, having(on(Person.class).getAge(),
    greaterThan(16)));
Copy the code

You can’t imagine anything easier to understand than that!

Answer 2:

Assuming you’re using Java 1.5 and you can’t use Google Collections, I’ll do something very similar to Google.

First add the following interface to your code:

public interface IPredicate<T> { boolean apply(T type); }
Copy the code

When judged to be true of some type, the implementer can return it. For example, if T is a User and AuthorizedUserPredicate < User > implementation IPredicate < T >, the AuthorizedUserPredicate. Apply to return if the User is authorized.

In some generic utility classes, you might write:

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;
}
Copy the code

So, you might implement the following:

Predicate<User> isAuthorized = new Predicate<User>() {
    public boolean apply(User user) {
        // binds a boolean method in User to a reference
        returnuser.isAuthorized(); }};// allUsers is a Collection<User>
Collection<User> authorizedUsers = filter(allUsers, isAuthorized);
Copy the code

If I’m concerned about the performance of linear checking, THEN I might need a domain object with a target collection. A domain object with a target collection will have filtering logic for methods to initialize, add, and set the target collection.

Update:

In the utility class (call Predicate), I’ve added a selection method that contains an option for default values if the Predicate doesn’t return the expected value, and a static property for the parameters used in the new IPredicate

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;
        }
        returnresult; }}Copy the code

The following example shows how to find 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; }});Copy the code

The following example shows looking for an instance in a collection and returning the first element of the collection as the default if no instance is found:

MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
    return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));
Copy the code

Update (since Java 8 release):

It’s been a few years since Alan first posted this answer, but I still can’t believe I’m collecting so many points for this answer. In any case, now that Java 8 has introduced closures for the language, my answer will be much different and much simpler. Using Java 8, you don’t need a unique static utility class. So, if you want to find the first element that satisfies the condition:

final UserService userService = ... // perhaps injected IoC
final Optional<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).findFirst();
Copy the code

The optional JDK 8 API has get(), isPresent(), orElse(defaultUser), OrElseGet (userSupplier) and orElseThrow(exceptionSupplier) as well as other single methods, such as map, flatMap, and Filter.

If you only want to filter all users that meet the criteria, use the collector to terminate the stream in the desired collection:

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

You can see more examples of how Java 8 streams work here.

The article translated from Stack Overflow:stackoverflow.com/questions/1…