Comparação de Strings em Java
Compare strings em Java corretamente com equals, equalsIgnoreCase, compareTo e entenda por que == compara referências.
Comparar duas strings parece a operação mais inocente da linguagem, mas é onde os novos programadores Java tropeçam com mais frequência. O motivo é que == é a sintaxe que parece óbvia para "são iguais?" — e para strings, ela responde uma pergunta que você quase nunca quer responder. Este capítulo percorre a API de comparação na ordem em que você deve utilizá-la e termina com as regras de ordenação — dobragem de maiúsculas, locale, strings numéricas e a armadilha de depender da ordenação padrão da JVM.
Por que == está errado para strings
== compara referências de objetos. Pergunta "essas duas variáveis apontam literalmente para o mesmo objeto no heap?". Para strings que compartilham identidade por meio do pool de strings, == acaba retornando true. Para qualquer outra coisa — strings construídas em tempo de execução, strings lidas de entrada, strings retornadas de new String(...) — ele retorna false mesmo quando o conteúdo é idêntico.
String a = "hello";
String b = "hello";
String c = new String("hello");
String d = "hel" + new String("lo");
a == b; // true — both pooled literals
a == c; // false — c is a fresh object
a == d; // false — d is built at runtime
a.equals(c); // true — contents match
a.equals(d); // true — contents matchA regra é curta e absoluta: para igualdade de strings, use equals. Sempre. Independentemente de onde as strings vieram. == "funciona por acaso" nos primeiros testes com literais e depois silenciosamente falha na primeira vez que uma string passa por I/O.
equals e equalsIgnoreCase
equals retorna true se e somente se as duas strings têm os mesmos caracteres na mesma ordem:
"hello".equals("hello"); // true
"hello".equals("Hello"); // false — case-sensitive
"hello".equals(null); // false — never throwsequalsIgnoreCase faz uma comparação caractere por caractere após a dobragem de maiúsculas Unicode:
"hello".equalsIgnoreCase("HELLO"); // true
"hello".equalsIgnoreCase("Hello"); // true
"straße".equalsIgnoreCase("STRASSE"); // false — ß and SS differ in lengthO exemplo com o ß alemão é um aviso útil: a dobragem de maiúsculas em Unicode não é uma bijeção (ß fica em minúsculo como ele mesmo, mas também corresponde a ss em alguns contextos). Se a entrada do usuário pode conter texto não ASCII e você precisa de correspondência sem distinção de maiúsculas, prefira normalizar ambos os lados com String#toLowerCase(Locale) antes de comparar, ou use java.text.Collator para correspondência sensível ao locale.
Comparação segura com null
equals em um receptor null lança NullPointerException. Portanto, isto é um bug:
if (input.equals("admin")) { ... } // NPE when input is nullTrês correções idiomáticas:
"admin".equals(input) // literal on the left — handles null safely
Objects.equals(input, "admin") // null-safe symmetric comparison (java.util.Objects)
Objects.requireNonNull(input).equals("admin") // fail-fast if null is a bug"literal".equals(variavel) — às vezes chamado de comparação Yoda — é o mais comum em bases de código estabelecidas. Objects.equals é ligeiramente mais elegante quando nenhum dos lados é estaticamente conhecido.
compareTo e compareToIgnoreCase
Quando você quer ordem (classificação, verificação de intervalo, busca binária), compareTo retorna um int:
< 0se o receptor vem antes do argumento na ordenação0se são iguais> 0se o receptor vem depois do argumento na ordenação
"apple".compareTo("banana"); // negative
"banana".compareTo("apple"); // positive
"apple".compareTo("apple"); // 0A comparação é feita por unidade de código Unicode (UTF-16), um caractere por vez, com strings mais curtas ordenadas antes das mais longas quando uma é prefixo da outra. Isso fornece uma ordem total definida — mas não a ordem que um humano chamaria de "alfabética":
"Z".compareTo("a"); // negative — 'Z' is 0x5A, 'a' is 0x61
"apple".compareTo("Banana"); // positive — uppercase letters come first in ASCIIPara ordenação voltada ao usuário, duas respostas reais:
compareToIgnoreCasepara a correção barata e voltada ao ASCII que funciona bem para o inglês.java.text.Collatorpara ordenação adequada e sensível ao locale que trata acentos, oßalemão, o lugar certo parañem espanhol e a convenção de que alguns scripts colocam numerais após as letras.
Collator c = Collator.getInstance(Locale.FRENCH);
List<String> names = new ArrayList<>(List.of("éclair", "Étoile", "anvil"));
names.sort(c); // ["anvil", "éclair", "Étoile"] — proper French sortSe um usuário vai ver a lista, use Collator. Se for uma chave de ordenação em um índice interno, compareTo é mais rápido e estável.
contentEquals e CharSequence
String#contentEquals compara com qualquer CharSequence — String, StringBuilder, StringBuffer ou um CharBuffer. É o método correto quando você quer saber se o conteúdo atual de um builder é igual a uma string conhecida sem uma chamada intermediária a toString():
StringBuilder sb = new StringBuilder("hello");
"hello".contentEquals(sb); // true — no toString allocationequals não funciona aqui, porque String#equals retorna false para qualquer argumento que não seja String por contrato.
equals ignora o pool completamente
O pool é uma otimização interna de armazenamento; equals não o consulta. Duas strings com os mesmos caracteres são comparadas como iguais independentemente de residirem no mesmo endereço ou não:
String pooled = "x";
String fresh = new String("x");
pooled == fresh; // false — different objects
pooled.equals(fresh); // true — same contents
fresh.hashCode() == pooled.hashCode(); // true — equal strings always hash the sameEssa última linha é a razão pela qual String é seguro para usar como chave de HashMap: strings iguais sempre têm códigos hash iguais, e o cache torna as pesquisas baratas.
Um exemplo prático
O programa percorre as decisões de comparação que você tomará em código real: escolher o método correto para igualdade, tratar null, ordenar com o comparador correto. A saída torna visíveis as diferenças entre compareTo e um Collator lado a lado.
As três ordenações tornam as diferenças visíveis. compareTo produz [Banana, anvil, apple, Étoile, éclair]: o Banana em maiúsculas vai para o início porque B (0x42) vem antes de qualquer letra minúscula, e Étoile fica perto do final porque É (0xC9) está acima de z. CASE_INSENSITIVE_ORDER produz [anvil, apple, Banana, éclair, Étoile], dobrando as maiúsculas para que a lista fique em ordem alfabética. Nesta lista específica, o Collator francês por acaso produz a mesma ordem — mas é o único cujo resultado é correto por construção: ele dobra as maiúsculas e trata acentos como distinção secundária, portanto permanece correto em entradas onde o truque do ponto de código falha (por exemplo, ordenar côté em relação a cote, ou posicionar ñ corretamente em espanhol).
O que vem a seguir
Dividir uma string em partes é o próximo tópico natural. Java tem duas ferramentas para isso, e uma delas é mais antiga do que o design da linguagem gostaria que você lembrasse. Continue em Java StringTokenizer.