¿Hay alguna razón por la que hay
Lists.transform()
pero no
Lists.filter()
?
¿Cómo filtro una lista correctamente? Podría usar
new ArrayList(Collection2.filter())
por supuesto, pero de esta manera no se garantiza que mi pedido sea el mismo, si lo entiendo correctamente.
List.newArrayList(Iterables.filter(...))
, debería decirLists.newArrayList(Iterables.filter(...))
.Respuestas:
No se implementó porque expondría una gran cantidad de métodos lentos peligrosos, como #get (index) en la vista de lista devuelta (invitando errores de rendimiento). Y ListIterator también sería complicado de implementar (aunque envié un parche hace años para cubrir eso).
Dado que los métodos indexados no pueden ser eficientes en la vista de lista filtrada, es mejor ir con un Iterable filtrado, que no los tiene.
fuente
filter
consistentemente significa una vista (junto con el comportamiento que implica) si eso esIterables.filter
,Sets.filter
etc. Dado que seIterables.filter
combina fácilmente concopyOf
cualquieraImmutableCollection
, encuentro que esta es una buena compensación de diseño (en lugar de crear métodos y nombres adicionales, comofilteredCopy
o lo que sea , para combinaciones de utilidades simples).Puede usar
Iterables.filter
, lo que definitivamente mantendrá el pedido.Tenga en cuenta que al construir una nueva lista, estará copiando los elementos (solo referencias, por supuesto), por lo que no será una vista en vivo de la lista original. Crear una vista sería bastante complicado; considere esta situación:
Predicate<StringBuilder> predicate = /* predicate returning whether the builder is empty */ List<StringBuilder> builders = Lists.newArrayList(); List<StringBuilder> view = Lists.filter(builders, predicate); for (int i = 0; i < 10000; i++) { builders.add(new StringBuilder()); } builders.get(8000).append("bar"); StringBuilder firstNonEmpty = view.get(0);
Eso tendría que iterar sobre toda la lista original, aplicando el filtro a todo. Supongo que podría requerir que la coincidencia de predicados no cambiara durante la vida útil de la vista, pero eso no sería del todo satisfactorio.
(Esto es solo una suposición, fíjate. Tal vez uno de los mantenedores de Guava contribuya con la verdadera razón :)
fuente
Collections2.filter.iterator
solo llamaIterables.filter
, por lo que el resultado es el mismo.Iterables.filter
versión solo para mayor claridad.view.size()
lugar más adelante en el código :)Eso no es cierto.
Collections2.filter()
es una función evaluada de forma perezosa: en realidad no filtra su colección hasta que comienza a acceder a la versión filtrada. Por ejemplo, si itera sobre la versión filtrada, los elementos filtrados saldrán del iterador en el mismo orden que su colección original (menos los filtrados, obviamente).Quizás estaba pensando que hace el filtrado por adelantado, luego vuelca los resultados en una Colección arbitraria y desordenada de alguna forma, no es así.
Así que si se utiliza la salida de
Collections2.filter()
como la entrada a una nueva lista, entonces su orden original será retenido.Usando importaciones estáticas (y la
Lists.newArrayList
función), se vuelve bastante conciso:Nótese que si bien
Collections2.filter
no con impaciencia iterar sobre la colección subyacente,Lists.newArrayList
será - se va a extraer todos los elementos de la colección filtrada y copiarlos en una nuevaArrayList
.fuente
List filteredList = newArrayList(filter(originalList, new Predicate<T>() { @Override public boolean apply(T input) { return (...); } }));
o es decir.List filteredList = newArrayList(filter(originalList, Predicates.notNull()));
Como mencionó Jon, puede usar
Iterables.filter(..)
oCollections2.filter(..)
y, si no necesita una vista en vivo, puede usarImmutableList.copyOf(Iterables.filter(..))
oLists.newArrayList( Iterables.filter(..))
y sí, se mantendrá el pedido.Si está realmente interesado en saber por qué parte, puede visitar https://github.com/google/guava/issues/505 para obtener más detalles.
fuente
Resumiendo lo que dijeron los demás, puede crear fácilmente un contenedor genérico para filtrar listas:
public static <T> List<T> filter(Iterable<T> userLists, Predicate<T> predicate) { return Lists.newArrayList(Iterables.filter(userLists, predicate)); }
fuente