Eu quero filtrar um com java.util.Collection
base em um predicado.
Eu quero filtrar um com java.util.Collection
base em um predicado.
Respostas:
O Java 8 ( 2014 ) resolve esse problema usando fluxos e lambdas em uma linha de código:
List<Person> beerDrinkers = persons.stream()
.filter(p -> p.getAge() > 16).collect(Collectors.toList());
Aqui está um tutorial .
Use Collection#removeIf
para modificar a coleção no local. (Aviso: Nesse caso, o predicado removerá objetos que satisfazem o predicado):
persons.removeIf(p -> p.getAge() <= 16);
O lambdaj permite filtrar coleções sem gravar loops ou classes internas:
List<Person> beerDrinkers = select(persons, having(on(Person.class).getAge(),
greaterThan(16)));
Você pode imaginar algo mais legível?
Disclaimer: Eu sou um colaborador no lambdaj
persons.removeIf(p -> p.getAge() <= 16);
Supondo que você esteja usando Java 1.5 e que não possa adicionar o Google Collections , eu faria algo muito semelhante ao que os caras do Google fizeram. Essa é uma pequena variação nos comentários de Jon.
Primeiro adicione essa interface à sua base de código.
public interface IPredicate<T> { boolean apply(T type); }
Seus implementadores podem responder quando um determinado predicado é verdadeiro para um determinado tipo. Por exemplo, se T
were User
e AuthorizedUserPredicate<User>
implementa IPredicate<T>
, então AuthorizedUserPredicate#apply
retorna se a passagem User
é autorizada.
Então, em alguma classe de utilidade, você poderia dizer
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;
}
Portanto, supondo que você tenha o uso do acima exposto pode 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);
Se o desempenho na verificação linear é motivo de preocupação, talvez eu queira ter um objeto de domínio que tenha a coleção de destino. O objeto de domínio que possui a coleção de destino teria lógica de filtragem para os métodos que inicializam, adicionam e definem a coleção de destino.
ATUALIZAR:
Na classe de utilitário (digamos Predicate), adicionei um método select com uma opção para o valor padrão quando o predicado não retorna o valor esperado, e também uma propriedade estática para os parâmetros serem usados dentro do novo 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;
}
}
O exemplo a seguir procura objetos ausentes entre coleções:
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;
}
});
O exemplo a seguir procura uma instância em uma coleção e retorna o primeiro elemento da coleção como valor padrão quando a instância não é encontrada:
MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));
UPDATE (após o lançamento do Java 8):
Faz vários anos desde que eu (Alan) publiquei esta resposta pela primeira vez, e ainda não consigo acreditar que estou coletando pontos SO para esta resposta. De qualquer forma, agora que o Java 8 introduziu fechamentos para a linguagem, minha resposta agora seria consideravelmente diferente e mais simples. Com o Java 8, não há necessidade de uma classe de utilitário estático distinta. Portanto, se você deseja encontrar o primeiro elemento que corresponde ao seu predicado.
final UserService userService = ... // perhaps injected IoC
final Optional<UserModel> userOption = userCollection.stream().filter(u -> {
boolean isAuthorized = userService.isAuthorized(u);
return isAuthorized;
}).findFirst();
O JDK 8 API para opcionais tem a capacidade de get()
, isPresent()
, orElse(defaultUser)
, orElseGet(userSupplier)
e orElseThrow(exceptionSupplier)
, bem como outras funções 'monádicos' tais como map
, flatMap
e filter
.
Se você deseja simplesmente coletar todos os usuários que correspondem ao predicado, use o Collectors
para finalizar o fluxo na coleção desejada.
final UserService userService = ... // perhaps injected IoC
final List<UserModel> userOption = userCollection.stream().filter(u -> {
boolean isAuthorized = userService.isAuthorized(u);
return isAuthorized;
}).collect(Collectors.toList());
Veja aqui para mais exemplos de como o Java 8 streams funciona.
val authorized = for (user <- users if user.isAuthorized) yield user
Use CollectionUtils.filter (Collection, Predicate) , do Apache Commons.
A "melhor" maneira é uma solicitação muito ampla. É o "mais curto"? "O mais rápido"? "Legível"? Filtrar no local ou em outra coleção?
A maneira mais simples (mas não a mais legível) é iterá-la e usar o método Iterator.remove ():
Iterator<Foo> it = col.iterator();
while( it.hasNext() ) {
Foo foo = it.next();
if( !condition(foo) ) it.remove();
}
Agora, para torná-lo mais legível, você pode agrupá-lo em um método utilitário. Em seguida, invente uma interface IPredicate, crie uma implementação anônima dessa interface e faça algo como:
CollectionUtils.filterInPlace(col,
new IPredicate<Foo>(){
public boolean keepIt(Foo foo) {
return foo.isBar();
}
});
em que filterInPlace () itera a coleção e chama Predicate.keepIt () para saber se a instância deve ser mantida na coleção.
Realmente não vejo uma justificativa para trazer uma biblioteca de terceiros apenas para esta tarefa.
stream()
recurso, mas nem todo mundo brinca com os brinquedos mais novos: P
Considere o Google Collections para obter uma estrutura de coleções atualizada que ofereça suporte a genéricos.
ATUALIZAÇÃO : a biblioteca de coleções do Google agora está obsoleta. Você deve usar a versão mais recente do Guava . Ele ainda possui as mesmas extensões da estrutura de coleções, incluindo um mecanismo de filtragem com base em um predicado.
Aguarde o 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 detalhado. Além disso, ouvi falar sobre começar a implementar APIs que aceitam e retornam Stream
s, em vez de Collection
s, porque Stream
s são muito úteis e rápidos, mas é muito lento ir / voltar deles.
Desde o lançamento inicial do Java 8, você pode tentar algo como:
Collection<T> collection = ...;
Stream<T> stream = collection.stream().filter(...);
Por exemplo, se você tivesse uma lista de números inteiros e desejasse filtrar os números> 10 e depois imprimir esses números no console, você poderia fazer algo como:
List<Integer> numbers = Arrays.asList(12, 74, 5, 8, 16);
numbers.stream().filter(n -> n > 10).forEach(System.out::println);
Vou jogar RxJava no ringue, que também está disponível no Android . O RxJava nem sempre é a melhor opção, mas oferece mais flexibilidade se você deseja adicionar mais transformações à sua coleção ou manipular erros durante a filtragem.
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);
}
});
Resultado:
1
3
5
Mais detalhes sobre o RxJava filter
podem ser encontrados aqui .
A configuração:
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();
}
}
}
O uso:
List<MyObject> myList = ...;
filterCollection(myList, new Predicate<MyObject>() {
public boolean filter(MyObject obj) {
return obj.shouldFilter();
}
});
Que tal um Java simples e direto
List<Customer> list ...;
List<Customer> newList = new ArrayList<>();
for (Customer c : list){
if (c.getName().equals("dd")) newList.add(c);
}
Simples, legível e fácil (e funciona no Android!) Mas se você estiver usando o Java 8, poderá fazê-lo em uma linha agradável:
List<Customer> newList = list.stream().filter(c -> c.getName().equals("dd")).collect(toList());
Observe que toList () é importado estaticamente
Tem certeza de que deseja filtrar a própria coleção, em vez de um iterador?
consulte org.apache.commons.collections.iterators.FilterIterator
ou usando a versão 4 do apache commons org.apache.commons.collections4.iterators.FilterIterator
Vejamos como filtrar uma lista JDK integrada e uma 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);
Se você deseja filtrar os números com menos de 3, esperaria as seguintes saídas.
List<Integer> selected = Lists.mutable.with(1, 2);
List<Integer> rejected = Lists.mutable.with(3, 4, 5);
Veja como você pode filtrar usando um Java 8 lambda como o 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));
Veja como você pode filtrar usando uma classe interna anônima como o 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));
Aqui estão algumas alternativas para filtrar listas JDK e listas mutáveis de coleções do Eclipse usando a fábrica de Predicados .
Assert.assertEquals(selected, Iterate.select(jdkList, Predicates.lessThan(3)));
Assert.assertEquals(selected, ecList.select(Predicates.lessThan(3)));
Aqui está uma versão que não aloca um objeto para o predicado, usando a fábrica Predicates2 em vez disso com o selectWith
método que leva a Predicate2
.
Assert.assertEquals(
selected, ecList.selectWith(Predicates2.<Integer>lessThan(), 3));
Às vezes, você deseja filtrar uma condição negativa. Existe um método especial no Eclipse Collections para esse chamado reject
.
Assert.assertEquals(rejected, Iterate.reject(jdkList, lessThan3));
Assert.assertEquals(rejected, ecList.reject(lessThan3));
O método partition
retornará duas coleções, contendo os elementos selecionados e rejeitados pelo 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: Sou um colaborador das Coleções Eclipse.
removeIf
em uma lista ou em um conjunto de primitivas?
Com o ForEach DSL, você pode escrever
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 uma coleção de [The, quick, brown, fox, saltos, over, the, preguiçoso, cachorro], isso resulta em [quick, brown, saltos, over, preguiçoso], ou seja, todas as strings com mais de três caracteres.
Todos os estilos de iteração suportados pelo ForEach DSL são
AllSatisfy
AnySatisfy
Collect
Counnt
CutPieces
Detect
GroupedBy
IndexOf
InjectInto
Reject
Select
Para mais detalhes, consulte https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach
O método Collections2.filter (Collection, Predicate) na biblioteca Guava do Google faz exatamente o que você está procurando.
Como o java 9 Collectors.filtering
está ativado:
public static <T, A, R>
Collector<T, ?, R> filtering(Predicate<? super T> predicate,
Collector<? super T, A, R> downstream)
Assim, a filtragem deve ser:
collection.stream().collect(Collectors.filtering(predicate, collector))
Exemplo:
List<Integer> oddNumbers = List.of(1, 19, 15, 10, -10).stream()
.collect(Collectors.filtering(i -> i % 2 == 1, Collectors.toList()));
Isso, combinado com a falta de fechamentos reais, é minha maior reclamação pelo Java. Honestamente, a maioria dos métodos mencionados acima é muito fácil de ler e REALMENTE eficiente; no entanto, depois de passar um tempo com .Net, Erlang, etc ..., a compreensão da lista integrada no nível do idioma torna tudo muito mais limpo. Sem acréscimos no nível da linguagem, o Java simplesmente não pode ser tão limpo quanto muitas outras linguagens nesta área.
Se o desempenho é uma grande preocupação, as coleções do Google são o caminho a seguir (ou escreva seu próprio utilitário de predicado simples). A sintaxe do Lambdaj é mais legível para algumas pessoas, mas não é tão eficiente.
E depois há uma biblioteca que escrevi. Ignorarei qualquer dúvida em relação à sua eficiência (sim, é tão ruim) ...... Sim, eu sei que é claramente baseado em reflexão, e não, na verdade não o uso, mas funciona:
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");
O JFilter http://code.google.com/p/jfilter/ é mais adequado para suas necessidades.
O JFilter é uma biblioteca de código aberto simples e de alto desempenho para consultar a coleção de Java beans.
Características principais
Eu escrevi uma classe Iterable estendida que suporta a aplicação de algoritmos funcionais sem copiar o conteúdo da coleção.
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);
}
O código acima realmente executará
for( int n : myList )
{
if( n % 2 == 0 )
{
System.out.println(n);
}
}
Use o mecanismo de consulta de coleção (CQEngine) . É de longe a maneira mais rápida de fazer isso.
Consulte também: Como você consulta coleções de objetos em Java (como Critérios / SQL)?
Algumas ótimas ótimas respostas aqui. Eu, gostaria de manter o mais simples e legível possível:
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();
}
}
}
}
}
A solução simples pré-Java8:
ArrayList<Item> filtered = new ArrayList<Item>();
for (Item item : items) if (condition(item)) filtered.add(item);
Infelizmente, essa solução não é totalmente genérica, produzindo uma lista em vez do tipo da coleção fornecida. Além disso, trazer bibliotecas ou funções de escrita que envolvem esse código parece um exagero para mim, a menos que a condição seja complexa, mas você pode escrever uma função para a condição.
https://code.google.com/p/joquery/
Suporta diferentes possibilidades,
Dada a coleção,
Collection<Dto> testList = new ArrayList<>();
do tipo
class Dto
{
private int id;
private String text;
public int getId()
{
return id;
}
public int getText()
{
return text;
}
}
Filtro
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();
Além disso,
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"});
Classificação (também disponível para o Java 7)
Filter<Dto> query = CQ.<Dto>filter(testList)
.orderBy()
.property(Dto::getId)
.property(Dto::getName)
Collection<Dto> sorted = query.list();
Agrupamento (também disponível para o Java 7)
GroupQuery<Integer,Dto> query = CQ.<Dto,Dto>query(testList)
.group()
.groupBy(Dto::getId)
Collection<Grouping<Integer,Dto>> grouped = query.list();
Junções (também disponíveis para o 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<>();
Pode ser juntado 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();
Expressões
Filter<Dto> query = CQ.<Dto>filter()
.from(testList)
.where()
.exec(s -> s.getId() + 1).eq().value(2);
Minha resposta baseia-se que a partir de Kevin Wong, aqui como uma one-liner usando CollectionUtils
de primavera e uma Java 8 lambda expressão.
CollectionUtils.filter(list, p -> ((Person) p).getAge() > 16);
Isso é tão conciso e legível quanto qualquer alternativa que eu tenha visto (sem usar bibliotecas baseadas em aspectos)
O Spring CollectionUtils está disponível na versão 4.0.2.RELEASE da primavera e lembre-se de que você precisa do JDK 1.8 e do nível de idioma 8+.
Usando java 8
, especificamente lambda expression
, você pode fazê-lo simplesmente como no exemplo abaixo:
myProducts.stream().filter(prod -> prod.price>10).collect(Collectors.toList())
onde, para cada coleção product
interna myProducts
, prod.price>10
adicione este produto à nova lista filtrada.
Eu precisava filtrar uma lista, dependendo dos valores já presentes na lista. Por exemplo, remova todos os valores seguintes que sejam menores que o valor atual. {2 5 3 4 7 5} -> {2 5 7}. Ou, por exemplo, para remover todas as duplicatas {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;
}
}
Isso será usado assim.
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;
}
});
Com goiaba:
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
No Java 8, você pode usar diretamente esse método de filtro e, em seguida, fazer isso.
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);