Quiero filtrar un java.util.Collection
basado en un predicado.
Quiero filtrar un java.util.Collection
basado en un predicado.
Respuestas:
Java 8 ( 2014 ) resuelve este problema usando flujos y lambdas en una línea de código:
List<Person> beerDrinkers = persons.stream()
.filter(p -> p.getAge() > 16).collect(Collectors.toList());
Aquí hay un tutorial .
Use Collection#removeIf
para modificar la colección en su lugar. (Aviso: en este caso, el predicado eliminará los objetos que satisfagan el predicado):
persons.removeIf(p -> p.getAge() <= 16);
lambdaj permite filtrar colecciones sin escribir bucles o clases internas:
List<Person> beerDrinkers = select(persons, having(on(Person.class).getAge(),
greaterThan(16)));
¿Te imaginas algo más legible?
Descargo de responsabilidad: soy colaborador de lambdaj
persons.removeIf(p -> p.getAge() <= 16);
Suponiendo que está utilizando Java 1.5 y que no puede agregar Google Collections , haría algo muy similar a lo que hicieron los chicos de Google. Esta es una ligera variación en los comentarios de Jon.
Primero agregue esta interfaz a su base de código.
public interface IPredicate<T> { boolean apply(T type); }
Sus implementadores pueden responder cuando cierto predicado es verdadero de cierto tipo. Por ejemplo, si T
estuviera User
e AuthorizedUserPredicate<User>
implemente IPredicate<T>
, AuthorizedUserPredicate#apply
devuelve si el pase User
está autorizado.
Luego, en alguna clase de utilidad, podrías decir
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;
}
Entonces, suponiendo que tiene el uso de lo anterior podría ser
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 el rendimiento en la verificación lineal es preocupante, entonces podría querer tener un objeto de dominio que tenga la colección de destino. El objeto de dominio que tiene la colección de destino tendría una lógica de filtrado para los métodos que inicializan, agregan y configuran la colección de destino.
ACTUALIZAR:
En la clase de utilidad (digamos Predicate), he agregado un método de selección con una opción para el valor predeterminado cuando el predicado no devuelve el valor esperado, y también una propiedad estática para los parámetros que se usarán dentro del nuevo 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;
}
}
El siguiente ejemplo busca objetos faltantes entre colecciones:
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;
}
});
El siguiente ejemplo busca una instancia en una colección y devuelve el primer elemento de la colección como valor predeterminado cuando no se encuentra la instancia:
MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));
ACTUALIZACIÓN (después del lanzamiento de Java 8):
Han pasado varios años desde que yo (Alan) publiqué esta respuesta por primera vez, y todavía no puedo creer que esté acumulando puntos SO para esta respuesta. En cualquier caso, ahora que Java 8 ha introducido cierres en el lenguaje, mi respuesta ahora sería considerablemente diferente y más simple. Con Java 8, no hay necesidad de una clase de utilidad estática distinta. Entonces, si desea encontrar el primer elemento que coincida con su predicado.
final UserService userService = ... // perhaps injected IoC
final Optional<UserModel> userOption = userCollection.stream().filter(u -> {
boolean isAuthorized = userService.isAuthorized(u);
return isAuthorized;
}).findFirst();
La API de JDK 8 para opcionales tiene la capacidad de get()
, isPresent()
, orElse(defaultUser)
, orElseGet(userSupplier)
y orElseThrow(exceptionSupplier)
, así como otras funciones 'monádicos' tales como map
, flatMap
y filter
.
Si desea simplemente recopilar todos los usuarios que coinciden con el predicado, utilice Collectors
para finalizar la secuencia en la colección deseada.
final UserService userService = ... // perhaps injected IoC
final List<UserModel> userOption = userCollection.stream().filter(u -> {
boolean isAuthorized = userService.isAuthorized(u);
return isAuthorized;
}).collect(Collectors.toList());
Consulte aquí para obtener más ejemplos sobre cómo funcionan las transmisiones Java 8.
val authorized = for (user <- users if user.isAuthorized) yield user
Utilice CollectionUtils.filter (Collection, Predicate) , de Apache Commons.
La "mejor" forma es una solicitud demasiado amplia. ¿Es "más corto"? "Lo más rápido"? "Legible"? ¿Filtrar en el lugar o en otra colección?
La forma más simple (pero no más legible) es iterarlo y usar el método Iterator.remove ():
Iterator<Foo> it = col.iterator();
while( it.hasNext() ) {
Foo foo = it.next();
if( !condition(foo) ) it.remove();
}
Ahora, para hacerlo más legible, puede envolverlo en un método de utilidad. Luego invente una interfaz IPredicate, cree una implementación anónima de esa interfaz y haga algo como:
CollectionUtils.filterInPlace(col,
new IPredicate<Foo>(){
public boolean keepIt(Foo foo) {
return foo.isBar();
}
});
donde filterInPlace () itera la colección y llama a Predicate.keepIt () para saber si la instancia se mantendrá en la colección.
Realmente no veo una justificación para traer una biblioteca de terceros solo para esta tarea.
stream()
función, pero no todos pueden jugar con los juguetes más nuevos: P
Considere Google Collections para un marco de Colecciones actualizado que admita genéricos.
ACTUALIZACIÓN : La biblioteca de colecciones de google ahora está en desuso. En su lugar, debe usar la última versión de Guava . Todavía tiene las mismas extensiones al marco de colecciones, incluido un mecanismo de filtrado basado en un predicado.
Espera a 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());
personList.removeIf(p -> p.age < 30);
Menos detallado. Además, he oído hablar de comenzar la ejecución de API que aceptar y volver Stream
s en lugar de Collection
s porque Stream
s son muy útiles y rápido, pero ir a / de ellos es lento.
Desde el lanzamiento temprano de Java 8, puede intentar algo como:
Collection<T> collection = ...;
Stream<T> stream = collection.stream().filter(...);
Por ejemplo, si tuviera una lista de enteros y quisiera filtrar los números que son> 10 y luego imprimir esos números en la consola, podría hacer algo como:
List<Integer> numbers = Arrays.asList(12, 74, 5, 8, 16);
numbers.stream().filter(n -> n > 10).forEach(System.out::println);
Voy a tirar RxJava en el anillo, que también está disponible en Android . RxJava puede no ser siempre la mejor opción, pero le dará más flexibilidad si desea agregar más transformaciones a su colección o manejar errores durante el filtrado.
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);
}
});
Salida:
1
3
5
Más detalles sobre RxJava filter
se pueden encontrar aquí .
La puesta en marcha:
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();
}
}
}
El uso:
List<MyObject> myList = ...;
filterCollection(myList, new Predicate<MyObject>() {
public boolean filter(MyObject obj) {
return obj.shouldFilter();
}
});
¿Qué tal un Java simple y directo?
List<Customer> list ...;
List<Customer> newList = new ArrayList<>();
for (Customer c : list){
if (c.getName().equals("dd")) newList.add(c);
}
Simple, legible y fácil (¡y funciona en Android!) Pero si está utilizando Java 8, puede hacerlo en una sola línea:
List<Customer> newList = list.stream().filter(c -> c.getName().equals("dd")).collect(toList());
Tenga en cuenta que toList () se importa estáticamente
¿Está seguro de que desea filtrar la Colección en sí, en lugar de un iterador?
ver org.apache.commons.collections.iterators.FilterIterator
o usando la versión 4 de apache commons org.apache.commons.collections4.iterators.FilterIterator
Veamos cómo filtrar una lista JDK incorporada y una lista MutableList usando Eclipse Collections .
List<Integer> jdkList = Arrays.asList(1, 2, 3, 4, 5);
MutableList<Integer> ecList = Lists.mutable.with(1, 2, 3, 4, 5);
Si quisiera filtrar los números menores que 3, esperaría los siguientes resultados.
List<Integer> selected = Lists.mutable.with(1, 2);
List<Integer> rejected = Lists.mutable.with(3, 4, 5);
A continuación, le mostramos cómo puede filtrar utilizando un Java 8 lambda como 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));
Así es como puede filtrar usando una clase interna anónima como 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));
Aquí hay algunas alternativas para filtrar listas JDK y Eclipse Collections MutableLists usando la fábrica de Predicados .
Assert.assertEquals(selected, Iterate.select(jdkList, Predicates.lessThan(3)));
Assert.assertEquals(selected, ecList.select(Predicates.lessThan(3)));
Aquí hay una versión que no asigna un objeto para el predicado, utilizando la fábrica Predicates2 en su lugar con el selectWith
método que toma a Predicate2
.
Assert.assertEquals(
selected, ecList.selectWith(Predicates2.<Integer>lessThan(), 3));
A veces quieres filtrar en una condición negativa. Hay un método especial en Eclipse Collections para eso llamado reject
.
Assert.assertEquals(rejected, Iterate.reject(jdkList, lessThan3));
Assert.assertEquals(rejected, ecList.reject(lessThan3));
El método partition
devolverá dos colecciones, que contienen los elementos seleccionados por y rechazados por 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());
Nota: Soy un committer para Eclipse Collections.
removeIf
en una lista o conjunto de primitivas?
Con ForEach DSL puedes escribir
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();
Dada una colección de [The, quick, brown, fox, jumps, over, the, lazy, dog], esto resulta en [quick, brown, jumps, over, lazy], es decir, todas las cadenas de más de tres caracteres.
Todos los estilos de iteración admitidos por ForEach DSL son
AllSatisfy
AnySatisfy
Collect
Counnt
CutPieces
Detect
GroupedBy
IndexOf
InjectInto
Reject
Select
Para obtener más detalles, consulte https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach
El método Collections2.filter (Collection, Predicate) en la biblioteca Guava de Google hace exactamente lo que estás buscando.
Como java 9 Collectors.filtering
está habilitado:
public static <T, A, R>
Collector<T, ?, R> filtering(Predicate<? super T> predicate,
Collector<? super T, A, R> downstream)
Por lo tanto, el filtrado debe ser:
collection.stream().collect(Collectors.filtering(predicate, collector))
Ejemplo:
List<Integer> oddNumbers = List.of(1, 19, 15, 10, -10).stream()
.collect(Collectors.filtering(i -> i % 2 == 1, Collectors.toList()));
Esto, combinado con la falta de cierres reales, es mi mayor queja para Java. Honestamente, la mayoría de los métodos mencionados anteriormente son bastante fáciles de leer y REALMENTE eficientes; sin embargo, después de pasar tiempo con .Net, Erlang, etc., la comprensión de listas integrada a nivel de lenguaje hace que todo sea mucho más limpio. Sin adiciones a nivel de lenguaje, Java no puede ser tan limpio como muchos otros idiomas en esta área.
Si el rendimiento es una gran preocupación, las colecciones de Google son el camino a seguir (o escriba su propia utilidad de predicado simple). La sintaxis de Lambdaj es más legible para algunas personas, pero no es tan eficiente.
Y luego hay una biblioteca que escribí. Ignoraré cualquier pregunta con respecto a su eficiencia (sí, es tan malo) ... Sí, sé que está claramente basado en la reflexión, y no, en realidad no lo uso, pero funciona:
LinkedList<Person> list = ......
LinkedList<Person> filtered =
Query.from(list).where(Condition.ensure("age", Op.GTE, 21));
O
LinkedList<Person> list = ....
LinkedList<Person> filtered = Query.from(list).where("x => x.age >= 21");
JFilter http://code.google.com/p/jfilter/ es el más adecuado para sus necesidades.
JFilter es una biblioteca de código abierto simple y de alto rendimiento para consultar la colección de beans Java.
Características clave
Escribí una clase Iterable extendida que admite la aplicación de algoritmos funcionales sin copiar el contenido de la colección.
Uso:
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);
}
El código anterior se ejecutará realmente
for( int n : myList )
{
if( n % 2 == 0 )
{
System.out.println(n);
}
}
Utilice el motor de consulta de recopilación (CQEngine) . Es, con mucho, la forma más rápida de hacer esto.
Consulte también: ¿Cómo consulta las colecciones de objetos en Java (Criterios / tipo SQL)?
Algunas respuestas realmente geniales aquí. A mí, me gustaría mantener las cosas tan simples y legibles como sea posible:
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();
}
}
}
}
}
La solución simple anterior a Java8:
ArrayList<Item> filtered = new ArrayList<Item>();
for (Item item : items) if (condition(item)) filtered.add(item);
Desafortunadamente, esta solución no es completamente genérica, ya que genera una lista en lugar del tipo de la colección dada. Además, traer bibliotecas o escribir funciones que envuelvan este código me parece excesivo a menos que la condición sea compleja, pero luego puede escribir una función para la condición.
https://code.google.com/p/joquery/
Admite diferentes posibilidades,
Dada la colección,
Collection<Dto> testList = new ArrayList<>();
de tipo,
class Dto
{
private int id;
private String text;
public int getId()
{
return id;
}
public int getText()
{
return text;
}
}
Filtrar
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();
También,
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"});
Clasificación (también disponible para Java 7)
Filter<Dto> query = CQ.<Dto>filter(testList)
.orderBy()
.property(Dto::getId)
.property(Dto::getName)
Collection<Dto> sorted = query.list();
Agrupación (también disponible para Java 7)
GroupQuery<Integer,Dto> query = CQ.<Dto,Dto>query(testList)
.group()
.groupBy(Dto::getId)
Collection<Grouping<Integer,Dto>> grouped = query.list();
Uniones (también disponible para Java 7)
Dado,
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<>();
Se puede unir como,
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();
Expresiones
Filter<Dto> query = CQ.<Dto>filter()
.from(testList)
.where()
.exec(s -> s.getId() + 1).eq().value(2);
Mi respuesta se basa en eso de Kevin Wong, aquí como una línea usando CollectionUtils
from spring y una expresión lambda de Java 8 .
CollectionUtils.filter(list, p -> ((Person) p).getAge() > 16);
Esto es tan conciso y legible como cualquier otra alternativa que haya visto (sin usar bibliotecas basadas en aspectos)
Spring CollectionUtils está disponible a partir de la versión 4.0.2.RELEASE de Spring, y recuerde que necesita JDK 1.8 y nivel de idioma 8+.
Utilizando java 8
, específicamente lambda expression
, puede hacerlo simplemente como en el siguiente ejemplo:
myProducts.stream().filter(prod -> prod.price>10).collect(Collectors.toList())
donde para cada colección product
interna myProducts
, si prod.price>10
, luego agregue este producto a la nueva lista filtrada.
Necesitaba filtrar una lista dependiendo de los valores ya presentes en la lista. Por ejemplo, elimine todos los valores siguientes que sean menores que el valor actual. {2 5 3 4 7 5} -> {2 5 7}. O, por ejemplo, para eliminar todos los duplicados {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;
}
}
Esto se utilizará así.
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;
}
});
Con guayaba:
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
En Java 8, puede usar directamente este método de filtro y luego hacerlo.
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);