W3docs

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 match

A 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 throws

equalsIgnoreCase 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 length

O 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 null

Trê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:

  • < 0 se o receptor vem antes do argumento na ordenação
  • 0 se são iguais
  • > 0 se o receptor vem depois do argumento na ordenação
"apple".compareTo("banana");        // negative
"banana".compareTo("apple");        // positive
"apple".compareTo("apple");         // 0

A 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 ASCII

Para ordenação voltada ao usuário, duas respostas reais:

  • compareToIgnoreCase para a correção barata e voltada ao ASCII que funciona bem para o inglês.
  • java.text.Collator para 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 sort

Se 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 CharSequenceString, 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 allocation

equals 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 same

Essa ú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.

java— editable, runs on the server

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.

Prática

Prática
Qual chamada para testar se um `input` (possivelmente `null`) é igual à string 'admin' é **ao mesmo tempo** correta e segura com `null`?
Qual chamada para testar se um `input` (possivelmente `null`) é igual à string 'admin' é **ao mesmo tempo** correta e segura com `null`?
Was this page helpful?