2010-07-07 12 views
2

Si j'ai un ArrayList<Double> dblList et Predicate<Double> IS_EVEN je suis en mesure d'éliminer tous les même éléments de dblList utilisant:Comment supprimer des éléments d'une collection transformée à l'aide d'un prédicat?

Collections2.filter(dblList, IS_EVEN).clear() 

si dblList est cependant le résultat d'une transformation comme

dblList = Lists.transform(intList, TO_DOUBLE) 

cela ne fonctionne pas d'autant plus que la liste transformée est immuable :-)

Toute solution?

Répondre

0

Peut-être:

Collection<Double> odds = Collections2.filter(dblList, Predicates.not(IS_EVEN)); 

ou

dblList = Lists.newArrayList(Lists.transform(intList, TO_DOUBLE)); 
Collections2.filter(dblList, IS_EVEN).clear(); 
+0

C'est bien si je suis uniquement intéressé par le résultat. Au lieu de cela, je veux vraiment modifier la collection originale indirectement. Ainsi, il est évident qu'une liste transformée ne supporte pas les opérations de modification comme set(). Mais il n'est pas clair pour moi pourquoi transformer() et filter() lui-même supporte une opération de suppression, une combinaison des deux ne le supporte pas. Je suis sur le point de degug this ... Mais ce n'est pas – Ditz

0

Tant que vous n'avez pas besoin pour la collecte intermédiaire, alors vous pouvez simplement utiliser Predicates.compose() pour créer un prédicat qui transforme d'abord l'élément, puis évalue un prédicat sur l'élément transformé.

Par exemple, supposons que j'ai une liste <Double> à partir de laquelle je veux supprimer tous les éléments où la partie Integer est pair. J'ai déjà une fonction < Double, Integer > qui me donne la partie Integer, et un prédicat <Entier> qui me dit si c'est pair.

je peux utiliser pour obtenir un nouveau prédicat, INTEGER_PART_IS_EVEN

Predicate<Double> INTEGER_PART_IS_EVEN = Predicates.compose(IS_EVEN, DOUBLE_TO_INTEGER); 
Collections2.filter(dblList, INTEGER_PART_IS_EVEN).clear();
+0

Pour cette tentative j'ai besoin de la liste originale (ou au moins la transformée). Malheureusement (dans mon cas) c'est encapsulé et je ne veux pas l'exposer. Je peux introduire un rappel pour effectuer la suppression en interne, mais cela ne peut être qu'une sorte de solution de contournement. A la place j'aime bien voir une opération Iterator.remove() en cours même sur des Collectios transformées et filtrées en cascade. – Ditz

0

Après quelques essais, je pense que je l'ai trouvé :)

final ArrayList<Integer> ints = Lists.newArrayList(1, 2, 3, 4, 5); 
Iterables.removeIf(Iterables.transform(ints, intoDouble()), even()); 
System.out.println(ints); 

[1,3,5] 
+0

mais je me demande pourquoi Iterables.removeIf (Lists.transform (ints, intoDouble()), even()); crash ... Je poste la question sur le groupe de discussion goyave –

2

Lists.transform() accepte une liste et retourne une obligeamment résultat qui est RandomAccess liste. Iterables.transform() accepte uniquement un Iterable et le résultat n'est pas RandomAccess. Enfin, Iterables.removeIf (et pour autant que je vois, c'est le seul dans Iterables) a une optimisation dans le cas où l'argument donné est RandomAccess, dont le point est de rendre l'algorithme linéaire au lieu de quadratique, par exemple. Pensez à ce qui se passerait si vous aviez un grand ArrayList (et non un ArrayDeque - qui devrait être plus populaire) et continuiez à supprimer des éléments de son début jusqu'à ce qu'il soit vide.

Mais l'optimisation ne dépend pas de itération remove(), mais de List.set(), ce qui ne peut pas être possiblement supporté dans une liste transformée. Si cela devait être corrigé, nous aurions besoin d'une autre interface de marqueur, pour indiquer que "l'ensemble optionnel() fonctionne réellement".

Ainsi, les options que vous avez sont:

  • appel de la version Iterables.removeIf(), et exécuter un algorithme quadratique (il ne sera pas grave si votre liste est petite ou vous supprimez quelques éléments)
  • Copiez la liste dans une autre liste qui prend en charge toutes les opérations facultatives, puis appelez Iterables.removeIf().
+0

Salut dimitris, explication claire, mais je ne vois pas de listes.removeIf() –

+0

Désolé, faute de frappe, signifiait Iterables.removeIf(). –

+0

Dans votre deuxième option, la copie n'aboutira pas à une baisse des performances? –

0

Je n'ai pas de solution, mais j'ai trouvé une sorte de problème avec Iterables.removeIf() en combinaison avec Lists.TransformingRandomAccessList.

La liste transformée implémente RandomAccess, ainsi Iterables.removeIf() délégués à Iterables.removeIfFromRandomAccessList() qui dépend d'une opération List.set() non prise en charge. L'appel Iterators.removeIf() serait toutefois réussi, car l'opération remove() IS est prise en charge par Lists.TransformingRandomAccessList.

voir: Iterables: 147

Conclusion: instanceof RandomAccess ne garantit pas List.set().

Addition: Dans des situations particulières appelant removeIfFromRandomAccessList() fonctionne même: si et seulement si les éléments à effacer forment un groupe compact à la queue de la liste ou tous les éléments sont couverts par la prédicats.

1

L'approche suivante devrait fonctionner, même si je ne l'ai pas encore essayé.

Collection<Double> dblCollection = 
    Collections.checkedCollection(dblList, Double.class); 
Collections2.filter(dblCollection, IS_EVEN).clear(); 

La méthode checkCollection() génère une vue de la liste qui n'implémente pas List. [Il serait plus propre, mais plus bavard, de créer un ForwardingCollection à la place.] Puis Collections2.filter() n'appelera pas la méthode set() non supportée.

Le code de bibliothèque pourrait être rendu plus robuste. Iterables.removeIf() pourrait générer un prédicat composé, comme Michael D l'a suggéré, lorsqu'il a passé une liste transformée. Cependant, nous avons précédemment décidé de ne pas compliquer le code en ajoutant une logique de cas particulier de ce type.