Estou implementando o compareTo()
método para uma classe simples como esta (para poder usar Collections.sort()
e outros brindes oferecidos pela plataforma Java):
public class Metadata implements Comparable<Metadata> {
private String name;
private String value;
// Imagine basic constructor and accessors here
// Irrelevant parts omitted
}
Quero que a ordem natural desses objetos seja: 1) classificada por nome e 2) classificada por valor, se o nome for o mesmo; ambas as comparações não diferenciam maiúsculas de minúsculas. Para ambos os campos, os valores nulos são perfeitamente aceitáveis, portanto, compareTo
não devem ser interrompidos nesses casos.
A solução que vem à mente é a seguinte: (estou usando "cláusulas de guarda" aqui, enquanto outros podem preferir um único ponto de retorno, mas isso não vem ao caso):
// primarily by name, secondarily by value; null-safe; case-insensitive
public int compareTo(Metadata other) {
if (this.name == null && other.name != null){
return -1;
}
else if (this.name != null && other.name == null){
return 1;
}
else if (this.name != null && other.name != null) {
int result = this.name.compareToIgnoreCase(other.name);
if (result != 0){
return result;
}
}
if (this.value == null) {
return other.value == null ? 0 : -1;
}
if (other.value == null){
return 1;
}
return this.value.compareToIgnoreCase(other.value);
}
Isso faz o trabalho, mas não estou perfeitamente feliz com esse código. É certo que não é muito complexo, mas é bastante detalhado e tedioso.
A questão é: como você tornaria isso menos detalhado (mantendo a funcionalidade)? Sinta-se à vontade para consultar as bibliotecas padrão Java ou o Apache Commons, se elas ajudarem. A única opção para tornar isso (um pouco) mais simples seria implementar meu próprio "NullSafeStringComparator" e aplicá-lo para comparar os dois campos?
Edições 1-3 : Eddie está certo; Corrigido o caso "ambos os nomes são nulos" acima
Sobre a resposta aceita
Eu fiz essa pergunta em 2009, no Java 1.6, é claro, e na época a solução JDK pura de Eddie era minha resposta preferida aceita. Eu nunca mudei para mudar isso até agora (2017).
Também existem soluções de bibliotecas de terceiros - uma das coleções Apache Commons 2009 e uma da Guava 2013, ambas postadas por mim - que eu preferia em algum momento.
Agora fiz a solução Java 8 limpa por Lukasz Wiktor a resposta aceita. Definitivamente, isso deve ser preferido no Java 8, e atualmente o Java 8 deve estar disponível para quase todos os projetos.
fonte
Respostas:
Usando o Java 8 :
fonte
Collections.sort(List)
não funcionará quando a Lista contiver nulos, o comentário não é relevante para a pergunta.Você pode simplesmente usar o Apache Commons Lang :
fonte
nullsFirst()
/nullsLast()
.org.apache.commons.lang3
) é "herdado / mal mantido / de baixa qualidade" é falsa ou, na melhor das hipóteses, infundada. O Commons Lang3 é fácil de entender e usar, e é mantido ativamente. É provavelmente a minha biblioteca mais usada (além do Spring Framework e Spring Security) - a classe StringUtils com seus métodos de segurança nula torna a normalização de entrada trivial, por exemplo.Eu implementaria um comparador seguro nulo. Pode haver uma implementação por aí, mas isso é tão simples de implementar que eu sempre desenvolvi o meu.
Nota: Seu comparador acima, se os dois nomes forem nulos, nem mesmo comparará os campos de valor. Eu não acho que é isso que você quer.
Eu implementaria isso com algo como o seguinte:
EDIT: erros corrigidos no exemplo de código. É isso que recebo por não testá-lo primeiro!
Edição: nullSafeStringComparator promovido para estático.
fonte
final
palavra-chave não é realmente necessária (o código Java já é detalhado como é.) No entanto, evita a reutilização de parâmetros como vars locais (uma péssima prática de codificação). Como nosso entendimento coletivo de software melhora com o tempo, sabemos que as coisas devem ser finais / const / imutáveis por padrão. Portanto, prefiro um pouco de verbosidade extra ao usarfinal
declarações de parâmetro (por mais trivial que seja a função) obterinmutability-by-quasi-default
.) A sobrecarga de compreensibilidade / manutenção é insignificante no grande esquema das coisas.Consulte a parte inferior desta resposta para obter a solução atualizada (2013) usando o Goiaba.
Foi com isso que eu finalmente fui. Acontece que já tínhamos um método utilitário para comparação de cadeias com segurança nula, portanto a solução mais simples era fazer uso disso. (É uma grande base de código; é fácil perder esse tipo de coisa :)
É assim que o auxiliar é definido (é sobrecarregado para que você também possa definir se os nulos vêm primeiro ou por último, se você quiser):
Portanto, isso é essencialmente o mesmo que a resposta de Eddie (embora eu não chamaria um método auxiliar estático de comparador ) e o de uzhin também.
De qualquer forma, em geral, eu teria favorecido fortemente a solução de Patrick , pois acho que é uma boa prática usar bibliotecas estabelecidas sempre que possível. ( Conheça e use as bibliotecas como Josh Bloch diz.) Mas, neste caso, isso não produziria o código mais limpo e simples.
Edit (2009): Versão do Apache Commons Collections
Na verdade, aqui está uma maneira de tornar a solução baseada no Apache Commons
NullComparator
mais simples. Combine-o com a distinção entre maiúsculas e minúsculasComparator
fornecida naString
classe:Agora isso é bem elegante, eu acho. (Apenas um pequeno problema permanece: o Commons
NullComparator
não suporta genéricos, então há uma tarefa não verificada.)Atualização (2013): versão do Goiaba
Quase cinco anos depois, eis como eu abordaria minha pergunta original. Se codificasse em Java, eu (é claro) estaria usando o Guava . (E certamente não o Apache Commons.)
Coloque essa constante em algum lugar, por exemplo, na classe "StringUtils":
Então, em
public class Metadata implements Comparable<Metadata>
:Obviamente, isso é quase idêntico à versão do Apache Commons (ambos usam o CASE_INSENSITIVE_ORDER do JDK ), o uso de
nullsLast()
ser a única coisa específica do Goiaba. Esta versão é preferível simplesmente porque o Goiaba é preferível, como uma dependência, ao Commons Collections. (Como todos concordam .)Se você estava pensando
Ordering
, observe que ele é implementadoComparator
. É bastante útil, especialmente para necessidades de classificação mais complexas, permitindo, por exemplo, encadear vários pedidos usandocompound()
. Leia Ordenação explicada para mais!fonte
ComparatorChain
não precisar de umcompareTo
método próprio .Eu sempre recomendo usar o Apache commons, pois provavelmente será melhor do que aquele que você pode escrever sozinho. Além disso, você pode fazer um trabalho "real" em vez de reinventar.
A classe em que você está interessado é o Comparador Nulo . Permite que você faça nulos altos ou baixos. Você também fornece seu próprio comparador para usar quando os dois valores não são nulos.
No seu caso, você pode ter uma variável de membro estática que faz a comparação e, em seguida, seu
compareTo
método apenas faz referência a isso.Algo como
}
Mesmo se você decidir criar sua própria, lembre-se dessa classe, pois ela é muito útil ao solicitar listas que contenham elementos nulos.
fonte
Eu sei que pode não ser uma resposta direta à sua pergunta, porque você disse que valores nulos precisam ser suportados.
Mas quero apenas observar que o suporte a nulos no compareTo não está alinhado com o contrato compareTo descrito nos javadocs oficiais do Comparable :
Portanto, eu lançaria NullPointerException explicitamente ou deixaria que fosse lançada pela primeira vez quando o argumento nulo estiver sendo desreferenciado.
fonte
Você pode extrair o método:
}
fonte
Você pode projetar sua classe para ser imutável (o Java 2º Ed. Efetivo tem uma ótima seção sobre isso, o Item 15: Minimizar a mutabilidade) e garantir na construção que nenhum nulo é possível (e usar o padrão de objeto nulo, se necessário). Em seguida, você pode pular todas essas verificações e assumir com segurança que os valores não são nulos.
fonte
Eu estava procurando por algo semelhante e isso pareceu um pouco complicado, então eu fiz isso. Eu acho que é um pouco mais fácil de entender. Você pode usá-lo como um comparador ou como um liner. Para esta pergunta, você mudaria para compareToIgnoreCase (). Como é, os nulos flutuam. Você pode virar o 1, -1 se quiser que eles afundem.
.
fonte
podemos usar o java 8 para fazer uma comparação amigável entre nulos entre objetos. suponho que eu tenho uma classe Boy com 2 campos: String name e Integer age e eu quero primeiro comparar nomes e depois envelhecer se ambos forem iguais.
e o resultado:
fonte
No caso de alguém usando o Spring, existe uma classe org.springframework.util.comparator.NullSafeComparator que também faz isso por você. Basta decorar o seu próprio comparável com ele assim
new NullSafeComparator<YourObject>(new YourComparable(), true)
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/comparator/NullSafeComparator.html
fonte
Para o caso específico em que você sabe que os dados não terão nulos (sempre é uma boa ideia para cadeias) e os dados são realmente grandes, você ainda está fazendo três comparações antes de comparar os valores, se tiver certeza de que esse é o seu caso , você pode otimizar um pouco. YMMV como código legível supera otimização menor:
fonte
saída é
fonte
Uma das maneiras simples de usar o NullSafe Comparator é usar a implementação do Spring, abaixo é um exemplo simples de referência:
fonte
Outro exemplo do Apache ObjectUtils. Capaz de classificar outros tipos de objetos.
fonte
Esta é a minha implementação que eu uso para classificar meu ArrayList. as classes nulas são classificadas até a última.
para o meu caso, o EntityPhone estende o EntityAbstract e meu contêiner é List <EntityAbstract>.
o método "compareIfNull ()" é usado para classificação segura nula. Os outros métodos são de integridade, mostrando como o compareIfNull pode ser usado.
fonte
Se você quer um simples hack:
Se você quiser colocar nulos no final da lista, basta alterar isso no método acima
fonte