Java et Predicates : une erreur de conception?

Dans un précédent article sur la programmation fonctionnelle, j'avais présenté les Predicate, utilisables en Java pour filtrer des listes selon des tests prédéfinis.

Il se trouve en réalité que ces objets n'aient pas réellement le comportement attendu... En effet, au lieu de simplement constituer une nouvelle liste à partir de celle fournit en paramètre et en écartant les objets ne satisfaisant pas le prédicat, une erreur est levée à chaque négation :

java.lang.IllegalArgumentException: Cannot add Object - Predicate
rejected it

Je trouve ce principe particulièrement étrange : cela implique que le prédicat ne permettra donc jamais de constituer une nouvelle liste... Pourquoi ne pas se contenter de renvoyer true/false sur le test? La question a également été soulevée sur ce forum, sans vraiment de suite...

Afin de contourner ce problème et d'obtenir réellement le comportement de filtre sur la liste, il faut utiliser l'objet org.apache.commons.collections.iterators.FilterIterator comme ceci :

List<Integer> list = Arrays.asList(5,8,12,11,65,2);
FilterIterator it = new FilterIterator(list.iterator(),
 new Predicate() {
  @Override
  public boolean evaluate(Object arg0) {
   return ((Integer)arg0).intValue()>10;
  }
 });
while(it.hasNext()) {
 System.out.println(it.next);
}
// Affichera : 12,11,65

Donc peut-être ai-je mal compris le fonctionnement des Predicate mais je pense que leur documentation gagnerait à expliciter ce genre de comportement...


Fichier(s) joint(s) :

4 commentaires:

Lynx a dit…

Je pense que c'est plus la signification des PredicatedList que celle des Predicate en eux-même qu'il faut revoir. La doc est assez claire à ce sujet : si un élément ne correspond pas au prédicat, blam, IllegalArgumentException. Et de préférence, créer la predicatedList avec une liste vide à la base (histoire de ne pouvoir la remplir que selon les critères du prédicat). La predicatedList a donc plus une utilité de vérification au remplissage que de filtrage.
Je ne connaissais pas FilterIterator sinon, c'est cool :-).
Quid de CollectionUtils.filter() qui marche bien avec un Predicate (mais pas avec n'importe quelle Collection !))?

Paul-Emmanuel Faidherbe a dit…

En effet, l'utilisation pour remplissage plutôt que pour filtrage à posteriori est sans doute plus probable.
Je n'ai pas pris le temps de regarder les autres librairies offrant les possibilités similaires de filtrage de listes, mais rien qu'au vu de leur nombre (Google collections, Apache utils...), on peut se demander quelles sont leur utilité...
Ont-elles toutes été mises en place en justifiant une utilité particulière ou est-ce simplement différents algorithmes...? A creuser.

Lynx a dit…

Les CollectionsUtils par Apache sont les plus anciennes. Google a par la suite décidé de faire une implémentation maison, tout simplement parce que la lib d'Apache ne supporte pas les Generics :-). Ce qui constitue un changement assez fondamental. Pour ce qui est des différences algorithmiques, j'ose espérer qu'elles ne sont pas majeures, mais ça reste un élément à investiguer :-).

Paul-Emmanuel Faidherbe a dit…

D'accord, je comprends mieux l'intérêt des lib Google sur ce point, effectivement c'est un apport conséquent.
J'espère également que les algorithmes ne sont pas trop différents, mais seule une réelle utilisation poussée (dans des contextes particuliers) doit pouvoir le déterminer.
En tout cas merci de ces précisions!