Cosa significa il termine "forma canonica" o "rappresentazione canonica" in Java?


91

Ho sentito spesso usare questo termine, ma non l'ho mai capito veramente.

Cosa significa e qualcuno può fornire alcuni esempi / indicarmi alcuni collegamenti?

EDIT: Grazie a tutti per le risposte. Puoi anche dirmi come la rappresentazione canonica è utile nelle prestazioni di equals (), come affermato in Effective Java?

Risposte:


58

Wikipedia punta al termine Canonicalizzazione .

Un processo per convertire i dati che hanno più di una possibile rappresentazione in una rappresentazione canonica "standard". Questo può essere fatto per confrontare diverse rappresentazioni per l'equivalenza, per contare il numero di strutture di dati distinte, per migliorare l'efficienza di vari algoritmi eliminando calcoli ripetuti o per rendere possibile imporre un ordinamento significativo.

L' esempio Unicode aveva più senso per me:

Le codifiche a lunghezza variabile nello standard Unicode, in particolare UTF-8, hanno più di una codifica possibile per i caratteri più comuni. Ciò rende la convalida della stringa più complicata, poiché deve essere considerata ogni possibile codifica di ogni carattere stringa. Un'implementazione software che non considera tutte le codifiche dei caratteri corre il rischio di accettare stringhe considerate non valide nella progettazione dell'applicazione, che potrebbero causare bug o consentire attacchi. La soluzione è consentire un'unica codifica per ogni carattere. La canonicalizzazione è quindi il processo di traduzione di ogni carattere stringa nella sua singola codifica consentita. Un'alternativa è che il software determini se una stringa è canonizzata e quindi la rifiuti se non lo è. In questo caso, in un contesto client / server, la canonicalizzazione sarebbe responsabilità del client.

In sintesi, una forma standard di rappresentazione dei dati. Da questo modulo puoi quindi convertire in qualsiasi rappresentazione tu possa aver bisogno.


64

Credo che ci siano due usi correlati del canonico: forme e istanze.

Una forma canonica significa che i valori di un particolare tipo di risorsa possono essere descritti o rappresentati in più modi e uno di questi modi viene scelto come forma canonica preferita. (Quella forma è canonizzata , come i libri che sono entrati nella Bibbia, e le altre forme no.) Un classico esempio di una forma canonica sono i percorsi in un file system gerarchico, in cui un singolo file può essere referenziato in diversi modi :

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

La definizione classica della rappresentazione canonica di quel file sarebbe l'ultimo percorso. Con percorsi locali o relativi non è possibile identificare globalmente la risorsa senza informazioni contestuali. Con i percorsi assoluti puoi identificare la risorsa, ma non puoi dire se due percorsi si riferiscono alla stessa entità. Con due o più percorsi convertiti nelle loro forme canoniche, puoi fare tutto quanto sopra, inoltre determinare se due risorse sono uguali o meno, se questo è importante per la tua applicazione (risolvi il problema di aliasing ).

Si noti che la forma canonica di una risorsa non è una qualità di quella particolare forma stessa; ci possono essere più forme canoniche possibili per un dato tipo come percorsi di file (diciamo, lessicograficamente prima di tutto possibili percorsi assoluti). Una forma viene selezionata come forma canonica per un particolare motivo di applicazione, o forse arbitrariamente in modo che tutti parlino la stessa lingua.

Forzare gli oggetti nelle loro istanze canoniche è la stessa idea di base, ma invece di determinare una rappresentazione "migliore" di una risorsa, sceglie arbitrariamente un'istanza di una classe di istanze con lo stesso "contenuto" del riferimento canonico, quindi converte tutti i riferimenti a oggetti equivalenti per utilizzare l'unica istanza canonica.

Questo può essere usato come tecnica per ottimizzare sia il tempo che lo spazio. Se in un'applicazione sono presenti più istanze di oggetti equivalenti, costringendoli a risolverli tutti come singola istanza canonica di un determinato valore, puoi eliminare tutti tranne uno di ciascun valore, risparmiando spazio e possibilmente tempo poiché ora puoi confrontare quei valori con identità di riferimento (==) in contrasto con l'equivalenza dell'oggetto ( equals()metodo).

Un classico esempio di ottimizzazione delle prestazioni con istanze canoniche è la compressione di stringhe con lo stesso contenuto. La chiamata String.intern()su due stringhe con la stessa sequenza di caratteri è garantita per restituire lo stesso oggetto String canonico per quel testo. Se passi tutte le tue stringhe attraverso quel canonicalizer, sai che le stringhe equivalenti sono in realtà riferimenti a oggetti identici, cioè alias

I tipi enum in Java 5.0+ costringono tutte le istanze di un particolare valore enum a utilizzare la stessa istanza canonica all'interno di una VM, anche se il valore è serializzato e deserializzato. Questo è il motivo per cui puoi usare if (day == Days.SUNDAY)impunemente in java if Daysè un tipo enum. Fare questo per le tue classi è certamente possibile, ma è necessario. Leggi Effective Java di Josh Bloch per dettagli e consigli.


32

Un buon esempio per comprendere "forma / rappresentazione canonica" è guardare la definizione del tipo di dati dello schema XML di "booleano":

  • la "rappresentazione lessicale" di booleano può essere una delle seguenti: {true, false, 1, 0}considerando
  • la "rappresentazione canonica" può essere solo una di {true, false}

Questo, in sostanza, significa quello

  • "true"e "1"vieni mappato alla riproduzione canonica. "true"e
  • "false"e "0"farti mappare alla riproduzione canonciale."false"

vedere la definizione del tipo di dati dello schema XML w3 per booleano


28

La parola "canonico" è solo un sinonimo di "standard" o "usuale". Non ha alcun significato specifico per Java.


3
canonico ha un significato più ricco rispetto allo standard o al solito IMO.
calamari

21

ridotta alla forma più semplice e significativa senza perdere la generalità


5

Un modo semplice per ricordarlo è il modo in cui "canonico" è usato nei circoli teologici, la verità canonica è la verità reale quindi se due persone la trovano hanno trovato la stessa verità. Lo stesso con l'istanza canonica. Se pensi di averne trovati due (cioè a.equals(b)) ne hai solo uno (cioè a == b). Quindi l'uguaglianza implica l'identità nel caso dell'oggetto canonico.

Ora per il confronto. Ora puoi scegliere di utilizzare a==b o a.equals(b) , poiché produrranno la stessa risposta nel caso dell'istanza canonica ma a == b è il confronto del riferimento (la JVM può confrontare due numeri estremamente rapidamente poiché sono solo due modelli a 32 bit confrontati a a.equals(b)cui è una chiamata al metodo e comporta un sovraccarico maggiore.


2

Un altro buon esempio potrebbe essere: hai una classe che supporta l'uso di coordinate cartesiane (x, y, z), sferiche (r, theta, phi) e cilindriche (r, phi, z). Allo scopo di stabilire l'uguaglianza (metodo dell'uguaglianza), probabilmente vorrai convertire tutte le rappresentazioni in una rappresentazione "canonica" di tua scelta, ad esempio coordinate sferiche. (O forse vorresti farlo in generale, cioè usare una rappresentazione interna.) Non sono un esperto, ma questo mi è venuto in mente forse come un buon esempio concreto.


0

rappresentazione canonica significa visualizzare il personaggio in uno stile diverso, ad esempio se scrivo una lettera A significa che un'altra persona può scrivere la lettera A in uno stile diverso :)

Ciò è conforme al CAMPO DI RICONOSCIMENTO DEI CARATTERI OTTICI



0

Le domande dell'OP sulla forma canonica e su come può migliorare le prestazioni del equalsmetodo possono essere risolte estendendo l'esempio fornito in Effective Java.

Considera la seguente 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);
  }
}

Il equalsmetodo in questo esempio ha aggiunto un costo utilizzando Stringil equalsIgnoreCasemetodo di. Come accennato nel testo

potresti voler memorizzare una forma canonica del campo in modo che il metodo uguale possa fare un confronto esatto economico su forme canoniche piuttosto che un confronto non standard più costoso.

Cosa intende Joshua Bloch quando dice forma canonica ? Ebbene, penso che la risposta concisa di Dónal sia molto appropriata. Possiamo memorizzare il Stringcampo sottostante CaseInsensitiveStringnell'esempio in un modo standard , forse la forma maiuscola di String. Ora, è possibile fare riferimento a questo forma canonica della CaseInsensitiveString, sua variante maiuscolo ed eseguire le valutazioni a buon mercato nelle vostre equalse hashcodemetodi.


0

Dati canonici in RDBMS, dati grafici;
Pensa come "normalizzazione" o "forma normale" di un dato in un RDBMS. Gli stessi dati esistono in tabelle diverse, rappresentati con un identificatore univoco e mappati in tabelle diverse.
o
Pensa a una singola forma di un dato nel database del grafico che rappresentasse in molti tripli.

Il vantaggio principale è rendere Dml (manipolazione dei dati) più efficiente poiché è possibile inserire (inserire / aggiornare) solo un valore anziché molti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.