Quelle est la meilleure façon de filtrer une collection Java?

674

Je veux filtrer un java.util.Collectionbasé sur un prédicat.

Kevin Wong
la source

Réponses:

699

Java 8 ( 2014 ) résout ce problème en utilisant des flux et des lambdas dans une seule ligne de code:

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

Voici un tutoriel .

Utilisez Collection#removeIfpour modifier la collection en place. (Remarque: dans ce cas, le prédicat supprimera les objets qui satisfont le prédicat):

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

lambdaj permet de filtrer les collections sans écrire de boucles ni de classes internes:

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

Pouvez-vous imaginer quelque chose de plus lisible?

Avertissement: je suis un contributeur sur lambdaj

Mario Fusco
la source
34
Bien mais les importations statiques obscurcissent ce qui se passe. Pour référence, select / having / on sont des importations statiques sur ch.lambdaj.Lambda, GreaterThan est org.hamcrest.Matchers
MikePatel
11
LambdaJ est vraiment sexy, mais il convient de noter qu'il implique un surcoût important (2,6 en moyenne): code.google.com/p/lambdaj/wiki/PerformanceAnalysis .
Doc Davluz
7
Apparemment, cela ne fonctionne pas sur Android: groups.google.com/forum/#!msg/lambdaj/km7uFgvSd3k/grJhgl3ik5sJ
Moritz
7
Vraiment comme cet exemple de LamdaJ ... similaire aux fonctions Lambda intégrées .NET. Et où peut-on boire à 16 ans? Nous devrions envisager d'ajouter une contrainte de localisation. : P
MAbraham1
3
removeIf exemple devrait êtrepersons.removeIf(p -> p.getAge() <= 16);
vim
224

En supposant que vous utilisez Java 1.5 et que vous ne pouvez pas ajouter de collections Google , je ferais quelque chose de très similaire à ce que les gars de Google ont fait. Ceci est une légère variation sur les commentaires de Jon.

Ajoutez d'abord cette interface à votre base de code.

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

Ses implémenteurs peuvent répondre lorsqu'un certain prédicat est vrai d'un certain type. Par exemple, si Tétaient Useret AuthorizedUserPredicate<User>implémente IPredicate<T>, puis AuthorizedUserPredicate#applyretourne si la transmission Userest autorisée.

Ensuite, dans une classe utilitaire, on pourrait dire

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

Donc, en supposant que vous utilisez ce qui précède, cela peut être

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

Si les performances de la vérification linéaire sont préoccupantes, je voudrais peut-être avoir un objet de domaine qui possède la collection cible. L'objet de domaine qui a la collection cible aurait une logique de filtrage pour les méthodes qui initialisent, ajoutent et définissent la collection cible.

MISE À JOUR:

Dans la classe utilitaire (disons Predicate), j'ai ajouté une méthode select avec une option pour la valeur par défaut lorsque le prédicat ne retourne pas la valeur attendue, ainsi qu'une propriété statique pour les paramètres à utiliser dans le nouveau 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;
        }
        return result;
    }
}

L'exemple suivant recherche les objets manquants entre les 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;
        }
    });

L'exemple suivant recherche une instance dans une collection et retourne le premier élément de la collection comme valeur par défaut lorsque l'instance est introuvable:

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

MISE À JOUR (après la sortie de Java 8):

Cela fait plusieurs années que j'ai (Alan) posté pour la première fois cette réponse, et je n'arrive toujours pas à croire que je collecte des points SO pour cette réponse. En tout cas, maintenant que Java 8 a introduit des fermetures au langage, ma réponse serait maintenant considérablement différente et plus simple. Avec Java 8, il n'est pas nécessaire d'avoir une classe d'utilitaire statique distincte. Donc, si vous voulez trouver le 1er élément qui correspond à votre prédicat.

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

Le 8 API pour JDK optionals a la capacité de get(), isPresent(), orElse(defaultUser), orElseGet(userSupplier)et orElseThrow(exceptionSupplier), ainsi que d' autres fonctions « monades » tels que map, flatMapet filter.

Si vous souhaitez simplement collecter tous les utilisateurs qui correspondent au prédicat, utilisez la Collectorspour terminer le flux dans la collection souhaitée.

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

Voir ici pour plus d'exemples sur le fonctionnement des flux Java 8.

Alan
la source
27
Ouais, mais je déteste réinventer la roue, encore et encore. Je préfère trouver une bibliothèque d'utilitaires qui fonctionne quand je veux.
Kevin Wong
2
Ce n'est pas le meilleur moyen au cas où vous ne voudriez pas de la nouvelle collection. Utilisez la métaphore de l'itérateur de filtre, qui peut être entrée dans une nouvelle collection, ou c'est tout ce dont vous avez besoin.
Josh
@Nestor: dans une compréhension Scala, le filtrage serait beaucoup plus simple:val authorized = for (user <- users if user.isAuthorized) yield user
Alan
Est-ce que cela modifie la collection originale ou en crée une toute nouvelle? J'ai essayé d'utiliser cette méthode et connecté mes deux collections (l'original et celui retourné par la méthode), ce sont les mêmes. @Alan
Rohan
1
@Rohan, ce n'est pas destiné à muter la collection d'origine. Notez que la collection de résultats ci-dessus est nouvellement construite et la méthode de filtrage ne s'ajoute à la collection de résultats que si le prédicat s'applique.
Alan
92

Utilisez CollectionUtils.filter (Collection, Predicate) , d'Apache Commons.

Kevin Wong
la source
3
c'est correct, mais ce n'est pas générique, et modifie la collection en place (pas sympa)
Kevin Wong
2
Il existe d'autres méthodes de filtrage dans CollectionUtils qui ne modifient pas la collection d'origine.
skaffman
42
En particulier, la méthode qui ne modifie pas la collection en place est org.apache.commons.collections.CollectionUtils # select (Collection, Predicate)
Eero
5
Dans Commons Collections v4, cela utilise désormais des génériques.
Justin Emery
1
Cette méthode doit être utilisée avec prudence car elle repose (au moins dans l'implémentation de commons-collections-3.2.1) sur la méthode iterator.remove () qui est facultative pour les collections, donc au lieu de filtrer, par exemple, un tableau, vous pourriez obtenez une exception UnsupportedOperationException.
user2417480
67

La "meilleure" façon est une demande trop large. Est-ce "le plus court"? "Le plus rapide"? "Lisible"? Filtrer en place ou dans une autre collection?

La manière la plus simple (mais pas la plus lisible) est de l'itérer et d'utiliser la méthode Iterator.remove ():

Iterator<Foo> it = col.iterator();
while( it.hasNext() ) {
  Foo foo = it.next();
  if( !condition(foo) ) it.remove();
}

Maintenant, pour le rendre plus lisible, vous pouvez l'envelopper dans une méthode utilitaire. Ensuite, inventez une interface IPredicate, créez une implémentation anonyme de cette interface et faites quelque chose comme:

CollectionUtils.filterInPlace(col,
  new IPredicate<Foo>(){
    public boolean keepIt(Foo foo) {
      return foo.isBar();
    }
  });

où filterInPlace () itère la collection et appelle Predicate.keepIt () pour savoir si l'instance doit être conservée dans la collection.

Je ne vois pas vraiment de justification pour faire appel à une bibliothèque tierce uniquement pour cette tâche.

Vladimir Dyuzhev
la source
6
Mon vote va pour celui-ci: cela fonctionne, sans bibliothèque externe. Je n'ai jamais réalisé que l'instanciation d'un Iterator pouvait être utile par rapport à l'utilisation de la syntaxe for-each, ou que vous pouviez supprimer des éléments d'une liste sans ConcurrentModificationException ou quelque chose comme ça. :)
ZeroOne
1
Je pense que c'est la meilleure façon d'utiliser la bibliothèque Java standard sans copier. Pour 1.8, il y aurait la stream()fonctionnalité, mais tout le monde ne peut pas jouer avec les nouveaux jouets: P
Populus
Est-ce que cela modifie également la collection originale? @ZeroOne
Rohan
Oui bien sûr, @Rohan. Essayez-le si vous n'y croyez pas. ;)
ZeroOne
Haha je l'ai fait! Mais je veux conserver ma collection d'origine. Pouvez-vous suggérer un moyen de le faire sans ajouter de bibliothèque externe? @ZeroOne
Rohan
62

Envisagez les collections Google pour un cadre de collections mis à jour qui prend en charge les génériques.

MISE À JOUR : La bibliothèque de collections Google est désormais obsolète. Vous devez utiliser la dernière version de Guava à la place. Il possède toujours les mêmes extensions du cadre des collections, y compris un mécanisme de filtrage basé sur un prédicat.

Frontières de la santé
la source
ya, je connaissais la bibliothèque Google collections. La version que j'utilisais ne contenait pas Collections2. J'ai ajouté une nouvelle réponse à cette question qui répertorie la méthode spécifique.
Kevin Wong
7
Kevin, Iterables.filter () et Iterators.filter () sont là depuis le début et sont généralement tout ce dont vous avez besoin.
Kevin Bourrillion
28

Attendez Java 8:

List<Person> 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());
gavenkoa
la source
13
Ugh ... c'est tellement verbeux. Pourquoi ne pourraient-ils pas simplement le faire: List <Person> result = personList.filter (p -> p.age> 30);
Kevin Wong
8
Pour utiliser le filtre directement sur la collection, vous devez utiliser removeIf call: download.java.net/jdk8/docs/api/java/util/…
gavenkoa
6
@KevinWong "verbeux" décrit à peu près tout le langage que je pense. Au moins, ils sont cohérents?
Rogue
5
Pourquoi ne pas utiliser Collectors.toList () dans la dernière partie?
Nestor Hernandez Loli
3
Voici un lien gavenkoa fourni qui ne fait pas 404. personList.removeIf(p -> p.age < 30);Moins verbeux. De plus, j'ai entendu parler de commencer à implémenter des API qui acceptent et renvoient des Streams plutôt que des Collections car les Streams sont très utiles et rapides mais aller vers / depuis eux est lent.
Captain Man
11

Depuis la première version de Java 8, vous pouvez essayer quelque chose comme:

Collection<T> collection = ...;
Stream<T> stream = collection.stream().filter(...);

Par exemple, si vous aviez une liste d'entiers et que vous vouliez filtrer les nombres> 10, puis les imprimer sur la console, vous pourriez faire quelque chose comme:

List<Integer> numbers = Arrays.asList(12, 74, 5, 8, 16);
numbers.stream().filter(n -> n > 10).forEach(System.out::println);
Josh M
la source
11

Je vais lancer RxJava dans le ring, qui est également disponible sur Android . RxJava n'est peut-être pas toujours la meilleure option, mais il vous donnera plus de flexibilité si vous souhaitez ajouter plus de transformations sur votre collection ou gérer les erreurs lors du filtrage.

Observable.from(Arrays.asList(1, 2, 3, 4, 5))
    .filter(new Func1<Integer, Boolean>() {
        public Boolean call(Integer i) {
            return i % 2 != 0;
        }
    })
    .subscribe(new Action1<Integer>() {
        public void call(Integer i) {
            System.out.println(i);
        }
    });

Production:

1
3
5

Plus de détails sur RxJava filterpeuvent être trouvés ici .

Brian Bowman
la source
7

La mise en place:

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

L'usage:

List<MyObject> myList = ...;
filterCollection(myList, new Predicate<MyObject>() {
  public boolean filter(MyObject obj) {
    return obj.shouldFilter();
  }
});
Jon
la source
2
Très bien, mais je préfère l'implémentation d'Alan parce que vous obtenez une copie de la collection au lieu de la modifier. De plus, le code d'Alan est thread-safe alors que le vôtre ne l'est pas.
marcospereira
7

Que diriez-vous de Java simple et direct

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

Simple, lisible et facile (et fonctionne sous Android!) Mais si vous utilisez Java 8, vous pouvez le faire en une seule ligne:

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

Notez que toList () est importé statiquement

Nestor Hernandez Loli
la source
7

Voyons comment filtrer une liste JDK intégrée et une liste MutableList à l' aide des collections Eclipse .

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

Si vous vouliez filtrer les nombres inférieurs à 3, vous vous attendriez aux sorties suivantes.

List<Integer> selected = Lists.mutable.with(1, 2);
List<Integer> rejected = Lists.mutable.with(3, 4, 5);

Voici comment filtrer en utilisant un lambda Java 8 comme 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));

Voici comment filtrer en utilisant une classe interne anonyme comme Predicate.

Predicate<Integer> lessThan3 = new Predicate<Integer>()
{
    public boolean accept(Integer each)
    {
        return each < 3;
    }
};

Assert.assertEquals(selected, Iterate.select(jdkList, lessThan3));
Assert.assertEquals(selected, ecList.select(lessThan3));

Voici quelques alternatives au filtrage des listes JDK et des collections Eclipse MutableLists à l'aide de la fabrique Predicates .

Assert.assertEquals(selected, Iterate.select(jdkList, Predicates.lessThan(3)));
Assert.assertEquals(selected, ecList.select(Predicates.lessThan(3)));

Voici une version qui n'alloue pas d'objet au prédicat, en utilisant la fabrique Predicates2 à la place avec la selectWithméthode qui prend a Predicate2.

Assert.assertEquals(
    selected, ecList.selectWith(Predicates2.<Integer>lessThan(), 3));

Parfois, vous souhaitez filtrer sur une condition négative. Il existe une méthode spéciale dans les collections Eclipse pour celle appelée reject.

Assert.assertEquals(rejected, Iterate.reject(jdkList, lessThan3));
Assert.assertEquals(rejected, ecList.reject(lessThan3));

La méthode partitionrenverra deux collections, contenant les éléments sélectionnés et rejetés par le Predicate.

PartitionIterable<Integer> jdkPartitioned = Iterate.partition(jdkList, lessThan3);
Assert.assertEquals(selected, jdkPartitioned.getSelected());
Assert.assertEquals(rejected, jdkPartitioned.getRejected());

PartitionList<Integer> ecPartitioned = gscList.partition(lessThan3);
Assert.assertEquals(selected, ecPartitioned.getSelected());
Assert.assertEquals(rejected, ecPartitioned.getRejected());

Remarque: je suis un committer pour les collections Eclipse.

Donald Raab
la source
1
Comment feriez-vous un removeIfsur une liste ou un ensemble de primitives?
Vivek Rao
L'API pour removeIf a été ajoutée aux collections primitives dans EC 9.1. eclipse.org/collections/javadoc/9.1.0/org/eclipse/collections/…
Donald Raab
5

Avec le ForEach DSL, vous pouvez écrire

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

Étant donné une collection de [The, quick, brown, fox, jumps, over, the, lazy, dog] cela se traduit par [quick, brown, jumps, over, lazy], c'est-à-dire toutes les chaînes de plus de trois caractères.

Tous les styles d'itération pris en charge par le ForEach DSL sont

  • AllSatisfy
  • AnySatisfy
  • Collect
  • Counnt
  • CutPieces
  • Detect
  • GroupedBy
  • IndexOf
  • InjectInto
  • Reject
  • Select

Pour plus de détails, veuillez vous référer à https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach

akuhn
la source
C'est assez intelligent! Beaucoup de travail pour implémenter une belle syntaxe Ruby-ish! Le négatif est que votre filtre n'est pas une fonction de première classe et ne peut donc pas être réutilisé. Roll on fermetures ...
oxbow_lakes
Bon point. Une façon de réutiliser le corps de la boucle consiste à refactoriser la boucle dans une méthode qui prend la requête de sélection en paramètre. Ce n'est cependant de loin pas aussi pratique et puissant que de véritables fermetures, c'est certain.
akuhn
5

Depuis java 9 Collectors.filtering est activé:

public static <T, A, R>
    Collector<T, ?, R> filtering(Predicate<? super T> predicate,
                                 Collector<? super T, A, R> downstream)

Le filtrage devrait donc être:

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

Exemple:

List<Integer> oddNumbers = List.of(1, 19, 15, 10, -10).stream()
            .collect(Collectors.filtering(i -> i % 2 == 1, Collectors.toList()));
yanefedor
la source
3

Ceci, combiné au manque de fermetures réelles, est mon plus gros reproche pour Java. Honnêtement, la plupart des méthodes mentionnées ci-dessus sont assez faciles à lire et vraiment efficaces; cependant, après avoir passé du temps avec .Net, Erlang, etc ... la compréhension de la liste intégrée au niveau de la langue rend tout tellement plus propre. Sans ajouts au niveau du langage, Java ne peut tout simplement pas être aussi propre que de nombreux autres langages dans ce domaine.

Si les performances sont une préoccupation majeure, les collections Google sont le chemin à parcourir (ou écrivez votre propre utilitaire de prédicat simple). La syntaxe Lambdaj est plus lisible pour certaines personnes, mais elle n'est pas aussi efficace.

Et puis il y a une bibliothèque que j'ai écrite. J'ignorerai toutes les questions concernant son efficacité (oui, c'est si mauvais) ...... Oui, je sais que c'est clairement basé sur la réflexion, et non je ne l'utilise pas vraiment, mais ça marche:

LinkedList<Person> list = ......
LinkedList<Person> filtered = 
           Query.from(list).where(Condition.ensure("age", Op.GTE, 21));

OU

LinkedList<Person> list = ....
LinkedList<Person> filtered = Query.from(list).where("x => x.age >= 21");
jdc0589
la source
Lien? Même si votre bibliothèque est inefficace ou inutilisable, il peut être intéressant de vérifier si la source est disponible.
MatrixFrog
Rendu public le dépôt ( net-machine.com/indefero/p/jdclib/source/tree/master ). Vous êtes intéressé par le package d'expression. Le package de test a un testeur avec un exemple d'utilisation. Je n'ai jamais vraiment travaillé sur l'interface de requête de chaîne référencée ci-dessus (je n'avais pas envie d'écrire un vrai analyseur), donc l'interface de requête explicite dans le testeur est la voie à suivre.
jdc0589
2

JFilter http://code.google.com/p/jfilter/ est le mieux adapté à vos besoins.

JFilter est une bibliothèque open source simple et hautes performances pour interroger la collection de beans Java.

Principales caractéristiques

  • Prise en charge des propriétés de collection (java.util.Collection, java.util.Map et Array).
  • Support de collection à l'intérieur de collection de n'importe quelle profondeur.
  • Prise en charge des requêtes internes.
  • Prise en charge des requêtes paramétrées.
  • Peut filtrer 1 million d'enregistrements en quelques 100 ms.
  • Le filtre (requête) est donné au format json simple, c'est comme les requêtes Mangodb. Voici quelques exemples.
  • {"id": {"$ le": "10"}
    • où la propriété id objet est inférieure à 10.
  • {"id": {"$ in": ["0", "100"]}}
    • où la propriété id objet est 0 ou 100.
  • {"lineItems": {"lineAmount": "1"}}
    • où la propriété de collection lineItems de type paramétré a lineAmount égal à 1.
  • {"$ et": [{"id": "0"}, {"billingAddress": {"city": "DEL"}}]}
    • où la propriété id est 0 et la propriété billingAddress.city est DEL.
  • {"lineItems": {"taxes": {"clé": {"code": "GST"}, "valeur": {"$ gt": "1.01"}}}}}
    • où la propriété de collection lineItems de type paramétré qui a des taxes carte la propriété de type de type paramétré a un code égal à une valeur de TPS supérieure à 1,01.
  • {'$ ou': [{'code': '10'}, {'skus': {'$ et': [{'prix': {'$ in': ['20', '40']} }, {'code': 'RedApple'}]}}]}
    • Sélectionnez tous les produits dont le code produit est 10 ou le prix sku en 20 et 40 et le code sku est "RedApple".
Kamran Ali Khan
la source
1
Vous devez nier que vous êtes l'auteur (comme je pense que c'est le cas).
assylias
Oui, je suis l'auteur de cette bibliothèque.
Kamran Ali Khan
2

J'ai écrit une classe Iterable étendue qui prend en charge l'application d'algorithmes fonctionnels sans copier le contenu de la collection.

Usage:

List<Integer> myList = new ArrayList<Integer>(){ 1, 2, 3, 4, 5 }

Iterable<Integer> filtered = Iterable.wrap(myList).select(new Predicate1<Integer>()
{
    public Boolean call(Integer n) throws FunctionalException
    {
        return n % 2 == 0;
    }
})

for( int n : filtered )
{
    System.out.println(n);
}

Le code ci-dessus s'exécutera réellement

for( int n : myList )
{
    if( n % 2 == 0 ) 
    {
        System.out.println(n);
    }
}
Vincent Robert
la source
2

Quelques très bonnes réponses ici. Moi, je voudrais garder les minces aussi simples et lisibles que possible:

public abstract class AbstractFilter<T> {

    /**
     * Method that returns whether an item is to be included or not.
     * @param item an item from the given collection.
     * @return true if this item is to be included in the collection, false in case it has to be removed.
     */
    protected abstract boolean excludeItem(T item);

    public void filter(Collection<T> collection) {
        if (CollectionUtils.isNotEmpty(collection)) {
            Iterator<T> iterator = collection.iterator();
            while (iterator.hasNext()) {
                if (excludeItem(iterator.next())) {
                    iterator.remove();
                }
            }
        }
    }
}
Lawrence
la source
Implémentez simplement l'excludeItem approprié par filtre. Vous finirez par avoir des filtres séparés exactement comme vous avez des trieurs sur les collections ...
Lawrence
1

La solution simple pré-Java8:

ArrayList<Item> filtered = new ArrayList<Item>(); 
for (Item item : items) if (condition(item)) filtered.add(item);

Malheureusement, cette solution n'est pas entièrement générique, produisant une liste plutôt que le type de la collection donnée. De plus, apporter des bibliothèques ou écrire des fonctions qui enveloppent ce code me semble exagéré à moins que la condition ne soit complexe, mais vous pouvez alors écrire une fonction pour la condition.

Andrew McKnight
la source
1

https://code.google.com/p/joquery/

Prend en charge différentes possibilités,

Étant donné la collection,

Collection<Dto> testList = new ArrayList<>();

de type,

class Dto
{
    private int id;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getText()
    {
        return text;
    }
}

Filtre

Java 7

Filter<Dto> query = CQ.<Dto>filter(testList)
    .where()
    .property("id").eq().value(1);
Collection<Dto> filtered = query.list();

Java 8

Filter<Dto> query = CQ.<Dto>filter(testList)
    .where()
    .property(Dto::getId)
    .eq().value(1);
Collection<Dto> filtered = query.list();

Aussi,

Filter<Dto> query = CQ.<Dto>filter()
        .from(testList)
        .where()
        .property(Dto::getId).between().value(1).value(2)
        .and()
        .property(Dto::grtText).in().value(new string[]{"a","b"});

Tri (également disponible pour Java 7)

Filter<Dto> query = CQ.<Dto>filter(testList)
        .orderBy()
        .property(Dto::getId)
        .property(Dto::getName)
    Collection<Dto> sorted = query.list();

Groupement (également disponible pour Java 7)

GroupQuery<Integer,Dto> query = CQ.<Dto,Dto>query(testList)
        .group()
        .groupBy(Dto::getId)
    Collection<Grouping<Integer,Dto>> grouped = query.list();

Jointures (également disponible pour Java 7)

Donné,

class LeftDto
{
    private int id;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getText()
    {
        return text;
    }
}

class RightDto
{
    private int id;
    private int leftId;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getLeftId()
        {
            return leftId;
        }

    public int getText()
    {
        return text;
    }
}

class JoinedDto
{
    private int leftId;
    private int rightId;
    private String text;

    public JoinedDto(int leftId,int rightId,String text)
    {
        this.leftId = leftId;
        this.rightId = rightId;
        this.text = text;
    }

    public int getLeftId()
    {
        return leftId;
    }

    public int getRightId()
        {
            return rightId;
        }

    public int getText()
    {
        return text;
    }
}

Collection<LeftDto> leftList = new ArrayList<>();

Collection<RightDto> rightList = new ArrayList<>();

Peut être rejoint comme,

Collection<JoinedDto> results = CQ.<LeftDto, LeftDto>query().from(leftList)
                .<RightDto, JoinedDto>innerJoin(CQ.<RightDto, RightDto>query().from(rightList))
                .on(LeftFyo::getId, RightDto::getLeftId)
                .transformDirect(selection ->  new JoinedDto(selection.getLeft().getText()
                                                     , selection.getLeft().getId()
                                                     , selection.getRight().getId())
                                 )
                .list();

Expressions

Filter<Dto> query = CQ.<Dto>filter()
    .from(testList)
    .where()
    .exec(s -> s.getId() + 1).eq().value(2);
Pélican volant à basse altitude
la source
1

Ma réponse s'appuie sur celle de Kevin Wong, ici en tant que doublure utilisant à CollectionUtilspartir du printemps et une expression lambda Java 8 .

CollectionUtils.filter(list, p -> ((Person) p).getAge() > 16);

C'est aussi concis et lisible que n'importe quelle alternative que j'ai vue (sans utiliser de bibliothèques basées sur l'aspect)

Spring CollectionUtils est disponible à partir de la version Spring 4.0.2.RELEASE, et n'oubliez pas que vous avez besoin du JDK 1.8 et du niveau de langue 8+.

vikingsteve
la source
1

En utilisant java 8, spécifiquement lambda expression, vous pouvez le faire simplement comme dans l'exemple ci-dessous:

myProducts.stream().filter(prod -> prod.price>10).collect(Collectors.toList())

où pour chaque collection productintérieure myProducts, si prod.price>10, puis ajoutez ce produit à la nouvelle liste filtrée.

hd84335
la source
1

J'avais besoin de filtrer une liste en fonction des valeurs déjà présentes dans la liste. Par exemple, supprimez toutes les valeurs suivantes inférieures à la valeur actuelle. {2 5 3 4 7 5} -> {2 5 7}. Ou par exemple pour supprimer tous les doublons {3 5 4 2 3 5 6} -> {3 5 4 2 6}.

public class Filter {
    public static <T> void List(List<T> list, Chooser<T> chooser) {
        List<Integer> toBeRemoved = new ArrayList<>();
        leftloop:
        for (int right = 1; right < list.size(); ++right) {
            for (int left = 0; left < right; ++left) {
                if (toBeRemoved.contains(left)) {
                    continue;
                }
                Keep keep = chooser.choose(list.get(left), list.get(right));
                switch (keep) {
                    case LEFT:
                        toBeRemoved.add(right);
                        continue leftloop;
                    case RIGHT:
                        toBeRemoved.add(left);
                        break;
                    case NONE:
                        toBeRemoved.add(left);
                        toBeRemoved.add(right);
                        continue leftloop;
                }
            }
        }

        Collections.sort(toBeRemoved, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });

        for (int i : toBeRemoved) {
            if (i >= 0 && i < list.size()) {
                list.remove(i);
            }
        }
    }

    public static <T> void List(List<T> list, Keeper<T> keeper) {
        Iterator<T> iterator = list.iterator();
        while (iterator.hasNext()) {
            if (!keeper.keep(iterator.next())) {
                iterator.remove();
            }
        }
    }

    public interface Keeper<E> {
        boolean keep(E obj);
    }

    public interface Chooser<E> {
        Keep choose(E left, E right);
    }

    public enum Keep {
        LEFT, RIGHT, BOTH, NONE;
    }
}

Ce sera utilisé comme ça.

List<String> names = new ArrayList<>();
names.add("Anders");
names.add("Stefan");
names.add("Anders");
Filter.List(names, new Filter.Chooser<String>() {
    @Override
    public Filter.Keep choose(String left, String right) {
        return left.equals(right) ? Filter.Keep.LEFT : Filter.Keep.BOTH;
    }
});
Fredrik Metcalf
la source
0

Avec la goyave:

Collection<Integer> collection = Lists.newArrayList(1, 2, 3, 4, 5);

Iterators.removeIf(collection.iterator(), new Predicate<Integer>() {
    @Override
    public boolean apply(Integer i) {
        return i % 2 == 0;
    }
});

System.out.println(collection); // Prints 1, 3, 5
ZhekaKozlov
la source
0

Dans Java 8, vous pouvez utiliser directement cette méthode de filtrage, puis le faire.

 List<String> lines = Arrays.asList("java", "pramod", "example");

 List<String> result = lines.stream()              
         .filter(line -> !"pramod".equals(line))     
         .collect(Collectors.toList());              

 result.forEach(System.out::println); 
pramod_m
la source