Qual é a melhor maneira de filtrar uma coleção Java?

672

Eu quero filtrar um com java.util.Collectionbase em um predicado.

Kevin Wong
fonte

Respostas:

698

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#removeIfpara 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

Mario Fusco
fonte
34
Bom, mas as importações estáticas ofuscam o que está acontecendo. Para referência, selecione / ter / on são importações estáticas sobre ch.lambdaj.Lambda, greaterThan é org.hamcrest.Matchers
MikePatel
11
O LambdaJ é realmente sexy, mas vale a pena notar que isso implica em uma sobrecarga significativa (média de 2,6): code.google.com/p/lambdaj/wiki/PerformanceAnalysis .
Doc Davluz 10/04/12
7
Aparentemente, não funciona no Android: groups.google.com/forum/#!msg/lambdaj/km7uFgvSd3k/grJhgl3ik5sJ
Moritz
7
Realmente como este exemplo de LamdaJ ... semelhante às funções Lambda internas do .NET. E onde uma pessoa pode beber aos 16 anos? Devemos considerar adicionar uma restrição de localização. : P
MAbraham1
3
removeIf exemplo deve serpersons.removeIf(p -> p.getAge() <= 16);
vim
223

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 Twere Usere AuthorizedUserPredicate<User>implementa IPredicate<T>, então AuthorizedUserPredicate#applyretorna 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, flatMape filter.

Se você deseja simplesmente coletar todos os usuários que correspondem ao predicado, use o Collectorspara 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.

Alan
fonte
27
Sim, mas eu odeio reinventar a roda, novamente, repetidamente. Eu prefiro encontrar alguma biblioteca de utilidades que faça quando eu quiser.
Kevin Wong
2
Esta não é a melhor maneira, caso você não queira a nova coleção. Use a metáfora do iterador de filtro, que pode ser inserida em uma nova coleção ou pode ser tudo o que você precisa.
Josh
@Nestor: em uma compreensão Scala, filtragem seria muito mais simples:val authorized = for (user <- users if user.isAuthorized) yield user
Alan
Isso modifica a coleção original ou cria uma nova? Tentei usar esse método e registrei minhas coleções (a original e a retornada do método), são iguais. @Alan
Rohan
1
@ Rohan, isso não pretende alterar a coleção original. Observe que a coleção de resultados acima foi recentemente construída e o método de filtro é adicionado à coleção de resultados somente se o predicado se aplicar.
Alan
92

Use CollectionUtils.filter (Collection, Predicate) , do Apache Commons.

Kevin Wong
fonte
3
isso é bom, mas não é genérica, e modifica a coleção no local (não agradável)
Kevin Wong
2
Existem outros métodos de filtro no CollectionUtils que não modificam a coleção original.
22410 skaffman
42
Em particular, o método que se não modificar a coleção no lugar é org.apache.commons.collections.CollectionUtils # selecionar (coleção, predicado)
Eero
5
No Commons Commons v4, agora ele usa Genéricos.
Justin Emery
1
Esse método deve ser usado com cautela, pois depende (pelo menos na implementação de commons-collections-3.2.1) do método iterator.remove (), que é opcional para as coleções, portanto, em vez de filtrar, por exemplo, uma matriz, você pode obtenha uma UnsupportedOperationException.
user2417480
67

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.

Vladimir Dyuzhev
fonte
6
Meu voto é para este: ele simplesmente funciona, sem bibliotecas externas. Eu nunca percebi que instanciar um Iterator poderia realmente ser útil em comparação ao uso da sintaxe de cada um ou que você pode remover itens de uma lista sem uma ConcurrentModificationException ou algo parecido. :)
ZeroOne
1
Eu acho que essa é a melhor maneira de usar a lib Java padrão sem copiar. Para a versão 1.8, haveria o stream()recurso, mas nem todo mundo brinca com os brinquedos mais novos: P
Populus
Isso também modifica a coleção original? @ZeroOne
Rohan
Sim, claro que sim, @Rohan. Experimente se você não acredita. ;)
ZeroOne 15/03
Haha eu fiz! Mas quero manter minha coleção original. Você pode sugerir uma maneira de fazer isso sem adicionar uma lib externa? @ZeroOne
Rohan
62

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.

Heath Borders
fonte
Eu sabia sobre a biblioteca de coleções do Google. A versão que eu estava usando não tinha Coleções2. Eu adicionei uma nova resposta a esta pergunta que lista o método específico.
Kevin Wong
7
Kevin, Iterables.filter () e Iterators.filter () estão lá desde o início e geralmente são tudo o que você precisa.
Kevin Bourrillion
28

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());
gavenkoa
fonte
13
Ugh ... é tão detalhado. Por que eles não podiam simplesmente fazer: List <Person> result = personList.filter (p -> p.age> 30);
Kevin Wong
8
Para usar o filtro diretamente na coleção que você precisa usar removeIf chamada: download.java.net/jdk8/docs/api/java/util/...
gavenkoa
6
@KevinWong "verbose" descreve praticamente todo o idioma que eu pensaria. Pelo menos eles são consistentes?
Rogue
5
Por que não usar Collectors.toList () na última parte?
Nestor Hernandez Loli
3
Aqui está um link gavenkoa, desde que não seja 404. personList.removeIf(p -> p.age < 30);Menos detalhado. Além disso, ouvi falar sobre começar a implementar APIs que aceitam e retornam Streams, em vez de Collections, porque Streams são muito úteis e rápidos, mas é muito lento ir / voltar deles.
Capitão Man
11

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);
Josh M
fonte
11

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 filterpodem ser encontrados aqui .

Brian Bowman
fonte
7

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();
  }
});
Jon
fonte
2
Tudo bem, mas eu prefiro a implementação do Alan porque você recebe uma cópia da coleção em vez de alterá-la. Além disso, o código de Alan é seguro para threads, enquanto o seu não.
Marcospereira 24/09/08
7

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

Nestor Hernandez Loli
fonte
7

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 selectWithmé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 partitionretornará 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.

Donald Raab
fonte
1
Como você faria removeIfem uma lista ou em um conjunto de primitivas?
Vivek Rao
A API para removeIf foi adicionada às coleções primitivas no EC 9.1. eclipse.org/collections/javadoc/9.1.0/org/eclipse/collections/…
Donald Raab
5

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

akuhn
fonte
Isso é muito inteligente! Muito trabalho para implementar uma boa sintaxe Ruby-ish! O negativo é que seu filtro não é uma função de primeira classe e, portanto, não pode ser reutilizado. Role em fechamentos ... #
4040 oxbow_lakes
Bom ponto. Uma maneira de reutilizar o corpo do loop é refatorando o loop em um método que usa a consulta de seleção como parâmetro. No entanto, isso não é de longe tão prático e poderoso quanto os fechamentos reais, com certeza.
akuhn
5

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()));
yanefedor
fonte
3

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");
jdc0589
fonte
Ligação? Mesmo que sua biblioteca seja ineficiente ou inutilizável, pode ser interessante verificar se a fonte está disponível.
MatrixFrog
Tornou o repositório público ( net-machine.com/indefero/p/jdclib/source/tree/master ). Você está interessado no pacote de expressões. O pacote de teste possui um testador com uso de exemplo. Eu realmente nunca trabalhei muito na interface de consulta de cadeias mencionada acima (não sentia vontade de escrever um analisador real); portanto, a interface de consulta explícita no testador é o caminho a seguir.
Jdc0589
2

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

  • Suporte de propriedades de coleção (java.util.Collection, java.util.Map e Array).
  • Suporte de coleta dentro de coleta de qualquer profundidade.
  • Suporte de consultas internas.
  • Suporte de consultas parametrizadas.
  • Pode filtrar 1 milhão de registros em poucos 100 ms.
  • O filtro (consulta) é fornecido no formato json simples, é como as consultas do Mangodb. A seguir estão alguns exemplos.
  • {"id": {"$ le": "10"}
    • onde a propriedade id do objeto é menor que igual a 10.
  • {"id": {"$ in": ["0", "100"]}}
    • onde a propriedade id do objeto é 0 ou 100.
  • {"lineItems": {"lineAmount": "1"}}
    • onde a propriedade de coleção lineItems do tipo parametrizado tem lineAmount igual a 1.
  • {"$ and": [{"id": "0"}, {"billingAddress": {"city": "DEL"}}]}
    • onde a propriedade id é 0 e a propriedade billingAddress.city é DEL.
  • {"lineItems": {"tax": {"key": {"code": "GST"}, "value": {"$ gt": "1.01"}}}}
    • onde a propriedade de coleção lineItems do tipo parametrizado que possui impostos, a propriedade do tipo de mapa do tipo parameteriszed possui código igual ao valor GST maior que 1,01.
  • {'$ or': [{'code': '10'}, {'skus': {'$ and': [{'price': {'$ in': ['20', '40']} }, {'code': 'RedApple'}]}}]}
    • Selecione todos os produtos em que o código do produto é 10 ou o preço do sku em 20 e 40 e o código do sku é "RedApple".
Kamran Ali Khan
fonte
1
Você deve se negar a ser o autor (como eu acho que é o caso).
Assilias
Sim, sou o autor desta biblioteca.
Kamran Ali Khan
2

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);
    }
}
Vincent Robert
fonte
2

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();
                }
            }
        }
    }
}
Lawrence
fonte
Basta implementar o excludeItem adequado por filtro. Você vai acabar tendo filtros separados exatamente como você tem classificadores em coleções ...
Lawrence
1

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.

Andrew McKnight
fonte
1

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);
Pelicano voador baixo
fonte
1

Minha resposta baseia-se que a partir de Kevin Wong, aqui como uma one-liner usando CollectionUtilsde 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+.

vikingsteve
fonte
1

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 productinterna myProducts, prod.price>10adicione este produto à nova lista filtrada.

hd84335
fonte
1

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;
    }
});
Fredrik Metcalf
fonte
0

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
ZhekaKozlov
fonte
0

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); 
pramod_m
fonte