Come gestire l'ordinamento di oggetti complessi?

7

Come si ordinerebbe una lista di oggetti che hanno più di un elemento ordinabile?

Supponiamo di avere un oggetto semplice Car e la macchina è definita come tale:

class Car {
    public String make;
    public String model;
    public int year;
    public String color;
    // ... No methods, just a structure / container
}

Ho progettato un framework semplice che avrebbe consentito di fornire più SortOption s a un Sorter che avrebbe poi ordinato l'elenco.

interface ISorter<T> {
    List<T> sort(List<T> items);
    void addSortOption(ISortOption<T> option);
    ISortOption<T>[] getSortOptions();
    void setSortOption(ISortOption<T> option);
}

interface ISortOption<T> {
    String getLabel();
    int compare(T t1, T t2);
}

Esempio di utilizzo

class SimpleStringSorter extends MergeSorter<String> {
    {
        addSorter(new AlphaSorter());
    }

    private static final class AlphaSorter implements ISortOption<String> {
        // ... implementation of alpha compare and get label
    }
}

Il problema con questa soluzione è che non è facilmente espandibile. Se la macchina dovesse mai ricevere un nuovo campo, ad esempio, currentOwner. Dovrei aggiungere il campo, quindi rintracciare il file della classe sorter, implementare una nuova classe di opzioni di ordinamento quindi ricompilare l'applicazione per la ridistribuzione.

C'è un modo più semplice e più espandibile / pratico per ordinare dati come questo?

    
posta ahodder 06.12.2012 - 23:03
fonte

2 risposte

8

In realtà puoi usare un comparatore che ha un metodo di confronto (a, b) che puoi implementare.

Quindi puoi passarlo per la fase di confronto (questo è supportato in quasi tutte le librerie standard della maggior parte delle lingue).

Ad esempio in java puoi chiamare

Collections.sort(fooList, new Comparator<Car>(){
    public int compare(Car a,Car b){
        return a.getModel().compareTo(b.getModel());
        // or compare what you want return -1, 0 or 1 
        // for less than, equal and greater than resp.
    }
});

Per ordinare i tuoi elenchi in base a un comparatore personalizzato

In java 8 c'è una sintassi lambda per creare il comparatore in una sola riga.

Questo significa che ci sarà solo un algoritmo di ordinamento da mantenere e un gruppo di comparatori che possono rimanere nella stessa classe di ciò che sta comparando (o vicino al luogo in cui avviene il confronto).

Ciò consente anche un ordinamento "a livelli", è possibile implementare qualcosa del tipo:

public static Comparator<T> createTieredComparator(final Comparator<? super T> comp1, final Comparator<? super T> comp2){
    return new Comparator<T>(){
        public int compare(T a,T b){
            int res = comp1.compare(a,b);
            if(res!=0)
                return res;
            else
                return comp2.compare(a,b);
        }
    };
}

Questo preferirà il confronto fatto da comp1 e restituirà solo il risultato di comp2 quando sarebbero considerati uguali secondo comp1 .

    
risposta data 07.12.2012 - 00:03
fonte
4

Implementa java.lang.Comparable e utilizza raccolte come java.util.TreeSet che si basano su "ordini naturali". L'implementazione del metodo compareTo () nell'interfaccia Comparable è molto simile a implementando un metodo equals () - che contiene il link un esempio di entrambi. È fondamentale implementare equals () e hashcode () se si implementa compareTo () e tutti e tre devono essere compatibili l'uno con l'altro!

private int compareNullHelper(Object a, Object b) {
    // sorts nulls first
    if (a == null) {
        if (b != null) { return -1; }
    } else if (b == null) { return 1; }
    return a.compareTo(b);
}

/** Sorts by make, model, year, color. */
@Override
public int compareTo(Car that) {
    // ensures consistency with equals()
    if (this.equals(that)) { return 0; }

    int ret = compareNullHelper(this.make, that.make);
    if (ret != 0) { return ret; }

    ret = compareNullHelper(this.model, that.model);
    if (ret != 0) { return ret; }

    ret = this.year - that.year;
    if (ret != 0) { return ret; }

    ret = compareNullHelper(this.color, that.color);
    if (ret != 0) { return ret; }

    // If you can't tell, return -1 which means that they are in the right
    // order but unequal - ensures consistency with equals()
    return -1;
}

Ora puoi scrivere:

Set<Car> orderedCars = new TreeSet<Car>();
orderedCars.addAll(myCars);

Se è necessario ordinare in base a criteri diversi, creare ulteriori classi di Comparator (ovvero un'altra interfaccia API Java) per ogni ordinamento. Se rendi l'implementazione Car comparabile e il metodo compareTo () della tua classe Car passa per marca, modello, anno, colore, potresti voler creare un ByColorComparator implements Comparator confrontato per colore, anno, marca, modello. Quindi dì:

Set<Car> carsByColor = new TreeSet<Car>(new ByColorComparator());
orderedCars.addAll(myCars);

L'ordinamento integrato nelle raccolte Java è incredibilmente veloce. Per anni le collezioni Java sono state una delle cose migliori della piattaforma.

Una cosa da notare è che questa tecnica funzionerà con Hibernate o JPA (se si dispone di una sessione di database aperta) per catturare automaticamente gli oggetti correlati dal database che potrebbero essere necessari per l'ordinamento.

    
risposta data 07.12.2012 - 00:02
fonte

Leggi altre domande sui tag