Contexto: Java moderno (a versão mais recente; JDK23 no momento em que esta pergunta foi escrita)
Dados 2 objetos comparáveis (implemento Comparable<T>
, onde T é um tipo que todos compartilham), como obter o maior dos dois?
Se esta pergunta fosse sobre números inteiros, a resposta seria razoavelmente simples:
int largest = Math.max(a, b);
É um pouco irritante (mas, talvez da perspectiva dos varargs que introduzem um array recém-alocado no heap, sensato devido a questões de desempenho) que isso funcione apenas para precisamente 2 números inteiros.
Mas não há max
para <T>
. Poderia existir; seria fácil:
public static <T implements Comparable<? super T>> T max(T a, T b) {
return a.compareTo(b) < 0 ? b : a;
}
Porém, java.lang.Math
não contém este método; apenas max
métodos de primitivos.
Complicações:
- Seria bom se você pudesse ter mais do que exatamente 2 valores, por exemplo, tendo a assinatura:
<T> T max(T... inputs)
. - Seria bom se, em vez de usar números naturalmente comparáveis, permitisse especificar um comparador personalizado.
Nesse sentido, eu esperava que esse método existisse em java.util.Comparator
:
public default T max(T... inputs) {
if (inputs.length == 0) throw new IllegalArgumentException("inputs is empty"); // [1]
T out = inputs[0];
for (int i = 1; i < inputs.length; i++) {
T t = inputs[i];
if (out.compareTo(t) < 0) out = t;
}
return out;
}
No entanto, nenhuma das classes em que se poderia esperar isso ( java.util.Comparator
, java.util.Comparators
, java.util.Comparable
) parece ter tal método.
Verificando alguns dos meus projetos de código, tenho alguns métodos espalhados, como:
public LocalDate latest(LocalDate a, LocalDate b) {
return a.isBefore(b) ? b : a;
}
que poderiam ser substituídos por tal método; LocalDate
implementa Comparable, portanto, se max
existisse um método, eu poderia simplesmente ter chamado max(date1, date2)
.
Existe em algum lugar nas java.*
bibliotecas principais? Existem planos em andamento em versões futuras do JDK para ter esse método?
Não estou pedindo bibliotecas específicas, apenas uma espécie de inventário: alguma biblioteca popular que o inclua, em termos tão genéricos quanto os trechos desta pergunta? 2
[1] Um designer pode inicialmente pensar que retornar an Optional<T>
é melhor aqui; este seria o design errado. Os chamadores, praticamente sempre, passariam argumentos explícitos, garantindo assim que opcional.none literalmente não pudesse acontecer. Pela mesma razão, é um projeto de API ruim criar um método que declare o lançamento de uma exceção verificada que os documentos dizem que nunca pode ocorrer em casos de uso comum, ter uma API que retorna um opcional que nunca pode ser NONE
é igualmente irritante. A exceção é, portanto, a solução correta aqui.
[2] null
o manuseio está em debate aqui. Se alguém atribuir a visão SQL de null
(que null
representa valores desconhecidos), então uma implementação que, por exemplo, retorne b
if a
is null
seria incorreta (como você sabe, entre um valor conhecido e um valor desconhecido, que o valor conhecido é aquele isso era maior?): Um NPE está correto. Mas, se alguém atribuir a visão que null
significa 'ausente', então NPE está errado e retornar o máximo entre os valores não nulos está correto. A questão já é bastante complexa; digamos que qualquer null
tratamento razoável seja aceitável.
Existem duas abordagens muito simples já facilmente montadas a partir da biblioteca padrão Java, e não é óbvio que sejam necessárias mais. Todos estes têm
min
versões alternativas.Stream.of(inputs).max(Comparator.naturalOrder())
, que aceita um arbitrárioComparator
e retorna umOptional
, que fica vazio se a coleção estiver vazia.NullPointerException
é lançado senull
for o elemento máximo.Collections.max(Arrays.asList(inputs))
, que corrige questões de segurança de tipo (para impor que você passe umComparator
, ou tenha elementos naturaisComparable
), mas retorna um elemento ou lançaNoSuchElementException
.null
será retornado se a entrada contiver nulo e o comparador fornecido relatar que o elemento máximo énull
.Já que você pergunta sobre bibliotecas, mencionarei aqui as ofertas do Guava:
Ordering
, que era o tipo "fluente" do GuavaComparator
, possuimax
métodos que aceitam umIterable
,Iterator
ou dois argumentos.Ordering
não está exatamente obsoleto, mas está obsoleto neste momento.Comparators
tem ummax
método que aceita doisComparable
argumentos ou dois argumentos e umComparator
.