martedì 29 marzo 2011

Java Comparable: usare indici con peso

Java dispone dell'interfaccia Comparable per permette la comparazione e l'ordinamento di oggetti in memoria. L'idea e' semplice: se un oggetto e' Comparable viene invocato un metodo particolare che deve restituire un valore positivo, negativo o nullo rispettivamente per sapere se tale oggetto e' "maggiore", "minore" o uguale a quello che si sta correntemente esaminando. La specifica pero' non pone nessun vincolo su quanto positivo o negativo debba essere un valore affinche' la comparazione sia valutata, e questo lascia ampio spazio di manovra ai programmatori per implementare in maniera semplice ed efficace i controlli di comparazione.
Si supponga ad esempio di avere un oggetto Document che debba essere ordinato per anno e mese. Il test formale prevede di controllare anzitutto gli anni di due documenti, e se questi coincidono, i rispettivi mesi. Quindi qualcosa di simile a:

public int compareTo(Document toCompare) {
    // controllo argomenti
    if( toCompare == null )
        return 1;

    if( getYear() == toCompare.getYear() ){
        // controllo mesi
        if( getMonth() > toCompare.getMonth() )
            return 1;
        else if( getMonth() < toCompare.getMonth() )
            return -1;
            else
            return 0;
    }
        else if( getYear() > toCompare.getYear() )
             return 1;
        else
         return 0;

}


In effetti il test effettuato con if/else e' abbastanza articolato e ricontrolla in vari punti condizioni simili. E' possibile ovviare a tutto quel codice considerando semplicemente che le due condizioni da testare (anno e mese) hanno pesi diversi: l'anno pesa di piu' (ossia una differenza fra gli anni e' discriminante indipendentemente dalla differenza fra i mesi), e quindi e' possibile calcolare un valore complessivo dato dalla differenza pesata fra i valori. Considerando che la differenza fra i mesi e' compresa fra -12 e +12 e' sufficiente che la differenza fra gli anni sia sempre maggiore a tale intervallo, e quindi:

public int compareTo(Document toCompare) {
    // controllo argomenti
    if( toCompare == null )
        return 1;

    int equality = 0;        // per default i due documenti sono uguali
    equality += ( getYear() - toCompare.getYear() ) * 100;
    equality += ( getMonth() - toCompare.getMonth() );
    return equality;

}



In questo modo una differenza fra gli anni sara' sempre superiore ad una differenza fra i mesi, che quindi sara' non considerata a meno che i due anni non risultino uguali.
In conclusione, considerando i range di differenza dei valori meno filtranti e' possibile costruire un semplice meccanismo di comparazione basato sulla sola differenza pesata dei valori stessi.

Nessun commento: