O que significa o termo “forma canônica” ou “representação canônica” em Java?

90

Já ouvi muitas vezes esse termo ser usado, mas nunca realmente o entendi.

O que significa, e alguém pode dar alguns exemplos / apontar-me alguns links?

EDIT: Obrigado a todos pelas respostas. Você também pode me dizer como a representação canônica é útil no desempenho de equals (), conforme declarado em Effective Java?

Shivasubramanian A
fonte

Respostas:

56

A Wikipedia aponta para o termo canonização .

Um processo para converter dados que têm mais de uma representação possível em uma representação canônica "padrão". Isso pode ser feito para comparar diferentes representações de equivalência, para contar o número de estruturas de dados distintas, para melhorar a eficiência de vários algoritmos, eliminando cálculos repetidos, ou para tornar possível impor uma ordem de classificação significativa.

O exemplo Unicode fez mais sentido para mim:

Codificações de comprimento variável no padrão Unicode, em particular UTF-8, têm mais de uma codificação possível para a maioria dos caracteres comuns. Isso torna a validação da string mais complicada, uma vez que cada codificação possível de cada caractere da string deve ser considerada. Uma implementação de software que não considera todas as codificações de caracteres corre o risco de aceitar strings consideradas inválidas no design do aplicativo, o que pode causar bugs ou permitir ataques. A solução é permitir uma única codificação para cada caractere. A canonização é então o processo de traduzir cada caractere de string para sua única codificação permitida. Uma alternativa é o software determinar se uma string é canonizada e rejeitá-la se não for. Nesse caso, em um contexto cliente / servidor, a canonização seria de responsabilidade do cliente.

Em resumo, uma forma padrão de representação de dados. A partir desse formulário, você pode converter para qualquer representação de que possa precisar.

Brian Gianforcaro
fonte
64

Acredito que haja dois usos relacionados de canônico: formas e instâncias.

Uma forma canônica significa que os valores de um tipo específico de recurso podem ser descritos ou representados de várias maneiras, e uma delas é escolhida como a forma canônica preferida. (Essa forma é canonizada , como os livros que entraram na Bíblia, e as outras formas não.) Um exemplo clássico de uma forma canônica são caminhos em um sistema de arquivos hierárquico, onde um único arquivo pode ser referenciado de várias maneiras :

myFile.txt                                   # in current working dir
../conf/myFile.txt                           # relative to the CWD
/apps/tomcat/conf/myFile.txt                 # absolute path using symbolic links
/u1/local/apps/tomcat-5.5.1/conf/myFile.txt  # absolute path with no symlinks

A definição clássica da representação canônica desse arquivo seria o último caminho. Com caminhos locais ou relativos, você não pode identificar globalmente o recurso sem informações contextuais. Com caminhos absolutos, você pode identificar o recurso, mas não pode dizer se dois caminhos se referem à mesma entidade. Com dois ou mais caminhos convertidos em suas formas canônicas, você pode fazer tudo acima, além de determinar se dois recursos são iguais ou não, se isso é importante para o seu aplicativo (resolva o problema de aliasing ).

Observe que a forma canônica de um recurso não é uma qualidade dessa forma particular; pode haver várias formas canônicas possíveis para um determinado tipo, como caminhos de arquivo (digamos, lexicograficamente antes de todos os caminhos absolutos possíveis). Um formulário é apenas selecionado como o formulário canônico para um motivo de aplicação específico, ou talvez arbitrariamente para que todos falem a mesma língua.

Forçar objetos em suas instâncias canônicas é a mesma ideia básica, mas em vez de determinar uma "melhor" representação de um recurso, ele escolhe arbitrariamente uma instância de uma classe de instâncias com o mesmo "conteúdo" da referência canônica e, em seguida, converte todas as referências a objetos equivalentes para usar a única instância canônica.

Isso pode ser usado como uma técnica para otimizar o tempo e o espaço. Se houver várias instâncias de objetos equivalentes em um aplicativo, ao forçar todos eles a serem resolvidos como a única instância canônica de um valor específico, você pode eliminar todos, exceto um de cada valor, economizando espaço e possivelmente tempo, pois agora você pode comparar aqueles valores com identidade de referência (==) em oposição à equivalência de objeto ( equals()método).

Um exemplo clássico de otimização de desempenho com instâncias canônicas é o recolhimento de strings com o mesmo conteúdo. Chamar String.intern()duas strings com a mesma sequência de caracteres certamente retornará o mesmo objeto String canônico para aquele texto. Se você passar todas as suas strings através desse canonicalizador, você saberá que strings equivalentes são, na verdade, referências a objetos idênticos, ou seja, aliases

Os tipos de enum em Java 5.0+ forçam todas as instâncias de um determinado valor de enum a usar a mesma instância canônica em uma VM, mesmo se o valor for serializado e desserializado. É por isso que você pode usar if (day == Days.SUNDAY)impunemente em java se Daysfor um tipo enum. Certamente é possível fazer isso em suas próprias aulas, mas tome cuidado. Leia Effective Java de Josh Bloch para obter detalhes e conselhos.

Dov Wasserman
fonte
31

Um bom exemplo para entender "forma / representação canônica" é examinar a definição de tipo de dados do esquema XML de "booleano":

  • a "representação lexical" do booleano pode ser um dos seguintes: {true, false, 1, 0}enquanto
  • a "representação canônica" só pode ser uma das {true, false}

Isso, em essência, significa que

  • "true"e "1"seja mapeado para a repr. canônica. "true"e
  • "false"e "0"sejam mapeados para o repr. canoncial."false"

consulte a definição de tipo de dados do esquema w3 XML para booleano

Michael Marton
fonte
28

A palavra "canônico" é apenas um sinônimo para "padrão" ou "usual". Não tem nenhum significado específico de Java.

Dónal
fonte
3
canônico tem um significado mais rico do que o padrão ou IMO usual.
lula
20

reduzido à forma mais simples e significativa sem perder a generalidade

Jaime
fonte
5

Uma maneira fácil de lembrar é a maneira como "canônico" é usado nos círculos teológicos, a verdade canônica é a verdade real, então se duas pessoas a encontrarem, terão encontrado a mesma verdade. Mesmo com a instância canônica. Se você acha que encontrou dois deles (ou seja, a.equals(b)) você realmente só tem um (ou seja a == b). Portanto, igualdade implica identidade no caso de objeto canônico.

Agora, para a comparação. Você agora tem a opção de usar a==b ou a.equals(b) , pois eles produzirão a mesma resposta no caso de instância canônica, mas a == b é a comparação da referência (a JVM pode comparar dois números extremamente rapidamente, pois eles são apenas dois padrões de 32 bits comparados para o a.equals(b)qual é uma chamada de método e envolve mais sobrecarga.

Chris Mawata
fonte
2

Outro bom exemplo pode ser: você tem uma classe que suporta o uso de coordenadas cartesianas (x, y, z), esféricas (r, teta, phi) e cilíndricas (r, phi, z). Para fins de estabelecer igualdade (método de igualdade), você provavelmente desejaria converter todas as representações em uma representação "canônica" de sua escolha, por exemplo, coordenadas esféricas. (Ou talvez você queira fazer isso em geral - ou seja, usar uma representação interna.) Não sou um especialista, mas isso me ocorreu como um bom exemplo concreto.

Kimberley Coburn
fonte
0

representação canônica significa visualizar o personagem em um estilo diferente, por exemplo, se eu escrever uma letra A, significa que outra pessoa pode escrever a letra A em um estilo diferente :)

Isso está de acordo com o CAMPO DE RECONHECIMENTO DE CARACTERES ÓPTICAS

SASIKALA
fonte
0

Uma forma canônica significa uma representação naturalmente única do elemento

Maksym Ovsianikov
fonte
0

As perguntas do OP sobre a forma canônica e como ele pode melhorar o desempenho do equalsmétodo podem ser respondidas estendendo o exemplo fornecido em Effective Java.

Considere a seguinte classe:

public final class CaseInsensitiveString {

  private final String s;

  public CaseInsensitiveString(String s) {
    this.s = Objects.requireNonNull(s);
  }

  @Override 
  public boolean equals(Object o) {
    return o instanceof CaseInsensitiveString && ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
  }
}

O equalsmétodo neste exemplo adicionou custo usando Stringo equalsIgnoreCasemétodo de. Conforme mencionado no texto

talvez você queira armazenar uma forma canônica do campo para que o método equals possa fazer uma comparação exata barata em formas canônicas, em vez de uma comparação não padrão mais cara.

O que Joshua Bloch quer dizer quando fala em forma canônica ? Bem, acho que a resposta concisa de Dónal é muito apropriada. Podemos armazenar o Stringcampo subjacente no CaseInsensitiveStringexemplo de uma forma padrão , talvez a forma em maiúsculas do String. Agora, você pode fazer referência a esta forma canônica de CaseInsensitiveString, sua variante em maiúsculas e realizar avaliações baratas em seus métodos equalse hashcode.

Adaga Gilbert Arenas
fonte
0

Dados canônicos em RDBMS, dados gráficos;
Pense como "Normalização" ou "Forma normal" de dados em um RDBMS. Os mesmos dados existem em tabelas diferentes, representados com um identificador único e mapeados em tabelas diferentes.
ou
Pense em uma única forma de dados no Graph Database que representou em muitos triplos.

O principal benefício disso é tornar o Dml (manipulação de dados) mais eficiente, pois você pode fazer o upsert (inserir / atualizar) apenas um valor em vez de muitos.

Alper t. Turker
fonte