Comparator e ordinamento per più campi

di il
9 risposte

Comparator e ordinamento per più campi

Ciao.
mi servirebbe una mano sui Comparator.
Se volessi ordinare una List per diversi campi, dovrei fare dei Comparator annidati tra loro? Sommare i risultato di "compareTo" non da l'effetto sperato, specialmente se ci sono numeri.
Esempio:
Classe Articolo che ha String tipo, String nome, Double prezzo

public class Articolo {
	private String tipo;
	private String nome;
	private double prezzo;
	....
	....
Se volessi ordinare la List di Articolo per tipo DESC, poi nome ASC e infine prezzo ASC, questo codice ovviamente non funziona

ArrayList<Articolo> lista = new ArrayList<>();
lista.add(new Articolo("Libro", "D", 12.25));
lista.add(new Articolo("Libro", "A", 23.86));
lista.add(new Articolo("Penna", "T", 2.69));
lista.add(new Articolo("Penna", "T", 25.12));
lista.add(new Articolo("Penna", "E", 123.86));

Comparator<Articolo> ordZA = 
	(Articolo a1, Articolo a2) -> a2.getTipo().compareTo(a1.getTipo())
			 + a1.getNome().compareTo(a2.getNome())
			 + (int)(a1.getPrezzo() - a2.getPrezzo());
Collections.sort(lista, ordZA);

//OPPURE SENZA LAMBDA
Comparator<Articolo> ordAZ = new Comparator<Articolo>() {
	@Override
	public int compare(Articolo a1, Articolo a2) {
		int res = a2.getTipo().compareTo(a1.getTipo());
		res += a1.getNome().compareTo(a2.getNome());
		res += a1.getPrezzo() - a2.getPrezzo();
		return res;
	}
};
esce:
Articolo [tipo=Libro, nome=D, prezzo=12.25]
Articolo [tipo=Penna, nome=T, prezzo=2.69]
Articolo [tipo=Libro, nome=A, prezzo=23.86]
Articolo [tipo=Penna, nome=T, prezzo=25.12]
Articolo [tipo=Penna, nome=E, prezzo=123.86]

vorrei la lista ordinata in questo modo (tipo DESC, nome ASC, prezzo ASC, ):

Articolo [tipo=Penna, nome=E, prezzo=123.86]
Articolo [tipo=Penna, nome=T, prezzo=2.69]
Articolo [tipo=Penna, nome=T, prezzo=25.12]
Articolo [tipo=Libro, nome=A, prezzo=23.86]
Articolo [tipo=Libro, nome=D, prezzo=12.25]

Anche perché la differenza tra il prezzo, influisce molto sul risultato dato dal compareTo sulle Stringhe
Come fare?
E' possibile?

Grazie
Tagan

9 Risposte

  • Re: Comparator e ordinamento per più campi

    tagan ha scritto:


    Se volessi ordinare una List per diversi campi, dovrei fare dei Comparator annidati tra loro?
    Precisiamo, se hai es. 3 campi da comparare E avessi GIÀ 3 Comparator uno per ciascun campo, allora tecnicamente si potrebbero anche comporre insieme, cioè fare un altro Comparator che li usa tutti e tre in "cascata".

    Ma altrimenti no, basta fare un singolo Comparator che gestisce le comparazioni in "cascata".

    tagan ha scritto:


    Sommare i risultato di "compareTo" non da l'effetto sperato
    Infatti è proprio SBAGLIATO.

    tagan ha scritto:


    Come fare?
    Fai le comparazioni in "cascata", come se ciascun campo avesse un proprio "peso".

    Prima compari i due dati x, se la comparazione dà "diverso", hai già il risultato.
    Se sono uguali, compari i due dati y, se la comparazione dà "diverso", hai già il risultato.
    Ecc...


    Vedi mio vecchio esempio qui: https://www.iprogrammatori.it/forum-programmazione/java/richiesta-informazioni-t31985-15.html#p8580668
  • Re: Comparator e ordinamento per più campi

    andbin ha scritto:


    Prima compari i due dati x, se la comparazione dà "diverso", hai già il risultato.
    Se sono uguali, compari i due dati y, se la comparazione dà "diverso", hai già il risultato.
    Ecc...
    Però.....potevo arrivarci !
    
    Comparator<Articolo> ordAZ = new Comparator<Articolo>() {
    	@Override
    	public int compare(Articolo a1, Articolo a2) {
    		int res = a1.getTipo().compareTo(a2.getTipo());
    		if (res!=0)
    			return res;
    		res = a1.getNome().compareTo(a2.getNome());
    		if (res!=0)
    			return res; 
    		res = (int)(a1.getPrezzo() - a2.getPrezzo());
    		return res;
    	}
    };
    
    Comparator<Articolo> ordZA = new Comparator<Articolo>() {
    	@Override
    	public int compare(Articolo a1, Articolo a2) {
    		int res = a2.getTipo().compareTo(a1.getTipo());
    		if (res!=0)
    			return res;
    		res = a1.getNome().compareTo(a2.getNome());
    		if (res!=0)
    			return res; 
    		res = (int)(a1.getPrezzo() - a2.getPrezzo());
    		return res;
    	}
    };
    
    funziona grazie.

    PS: si può scrivere con le espressioni lambda? (.....è solo curiosità)

    Grazie.
    Tagan
  • Re: Comparator e ordinamento per più campi

    tagan ha scritto:


    
    Comparator<Articolo> ordAZ = new Comparator<Articolo>() {
    	@Override
    	public int compare(Articolo a1, Articolo a2) {
    		int res = a1.getTipo().compareTo(a2.getTipo());
    		if (res!=0)
    			return res;
    		res = a1.getNome().compareTo(a2.getNome());
    		if (res!=0)
    			return res; 
    		res = (int)(a1.getPrezzo() - a2.getPrezzo());
    		return res;
    	}
    };
    Di per sé ok. A me personalmente piace avere un singolo return al fondo (come in quel vecchio esempio). Ma lì è questione di gusti ....

    Invece NON fare la differenza tra due valori!
    O fai la comparazione con < > in modo compatto con l'operatore "condizionale" oppure meglio ancora sfrutti i nuovi metodi di Integer, Double ecc... disponibili da Java 7:

    Integer.compare(x, y)
    Double.compare(x, y)
    ecc..

    tagan ha scritto:


    
    Comparator<Articolo> ordZA = new Comparator<Articolo>() {
    
    Se non ci sono motivi validissimi, non sprecare codice per fare l' "inverso".

    Puoi sfruttare il metodo di Collections da Java 5:
    public static <T> Comparator<T> reverseOrder(Comparator<T> cmp)

    oppure da Java 8 Comparator ha il reversed() di default

    tagan ha scritto:


    PS: si può scrivere con le espressioni lambda? (.....è solo curiosità)
    Implementando tu il Comparator "a mano" come anonymous inner class, c'è ben poco (nulla) che puoi fare con le lambda.

    Altrimenti da Java 8 puoi sfruttare le novità in Comparator, cioè tutti i nuovi comparing() e thenComparing() introdotti di default (vedi javadoc per i dettagli) dove puoi sfruttare anche meglio i method-references. Ma attenzione è un po' meno efficiente che fare tu le comparazioni.
  • Re: Comparator e ordinamento per più campi

    andbin ha scritto:


    Invece NON fare la differenza tra due valori!
    O fai la cosa in modo compatto con l'operatore "condizionale" oppure meglio ancora sfrutti i nuovi metodi di Integer, Double ecc... disponibili da Java 7:

    Integer.compare(x, y)
    Double.compare(x, y)
    ecc..
    ah perfetto grazie.
    ho fatto cosi, perchémolti esempi sul web riportano nel comapre, codice del tipo p1.getAge() - p2.getAge() dove p è istanza di una classe Person
    quindi pensavo si potesse fare, anche perché si dice che p1 è < di p2 se il risultato del compare è <0 , uguale se =0, maggiore se >0
    anche se per le stringhe, ad esempio, restuisce -1 , 0 oppure 1
    ho modificato in modo che restituisco sempre -1, 1 or 0

    andbin ha scritto:


    Puoi sfruttare il metodo di Collections da Java 5:
    public static <T> Comparator<T> reverseOrder(Comparator<T> cmp)

    oppure da Java 8 Comparator ha il reversed() di default

    Altrimenti da Java 8 puoi sfruttare le novità in Comparator, cioè tutti i nuovi comparing() e thenComparing() introdotti di default (vedi javadoc per i dettagli) dove puoi sfruttare anche meglio i method-references.
    Il libro che sto studiano parla di java 8, ma non riporta tutti i nuovi metodi che mi hai citato tu. forse dovrei dare un'occhiata anche alla documentazione ufficiale

    il mio obiettivo ora è di farmi assegnare per l'anno prossimo a un progetot java invece di rimanere fossilizzato a Natural.
    Però in azienda fanno web application. Si usa java 7 al massimo giusto?
    e se dopo aver studiato bene SE, volessi approcciarmi a java EE, conviene studiare i framework, corretto?
    non ho trovato un buon libro su java EE da nessuna parte, tutte infarinature e tutti in inglese.

    Grazie mille.
    Tagan
  • Re: Comparator e ordinamento per più campi

    tagan ha scritto:


    ho modificato in modo che restituisco sempre -1, 1 or 0
    No, non serve.

    tagan ha scritto:


    Il libro che sto studiano parla di java 8, ma non riporta tutti i nuovi metodi che mi hai citato tu. forse dovrei dare un'occhiata anche alla documentazione ufficiale
    Sì, sarebbe sicuramente meglio.

    tagan ha scritto:


    Però in azienda fanno web application. Si usa java 7 al massimo giusto?
    Beh, no ... dipende. Si usa anche Java 8. Dove lavoro adesso uso Java 8 per webapp con Spring Boot.

    tagan ha scritto:


    e se dopo aver studiato bene SE, volessi approcciarmi a java EE
    Prima di Java EE dovrai farti un bel po' di esperienza con Java (e Java SE) ....

    tagan ha scritto:


    non ho trovato un buon libro su java EE da nessuna parte
    Perché non c'è un singolo libro su "tutto" Java EE.

    tagan ha scritto:


    tutte infarinature e tutti in inglese.
    Per l'inglese purtroppo sì, è un contesto molto specifico.
  • Re: Comparator e ordinamento per più campi

    andbin ha scritto:


    Prima di Java EE dovrai farti un bel po' di esperienza con Java (e Java SE) ....
    Come me la faccio l'esperienza in Java SE ? è possibile usarlo anche in ambiente EE.
    Per quello che ho capito (non so se può esserti utile) stanno dismettendo JSP, (possibile?) parlano di fare l'html con PrimeFaces. Ma dovrei sapere anche cosa è un servlet che a oggi per me è solo un nome...... cmq, con calma...

    Ultima domanda invece, tornando In Topic:
    Premessa (dimmi se dico qualche stupidaggine):
    - Comparable è una interfaccia che necessita l'override di compareTo(T o)
    - Comparator è sempre una interfaccia e necessiata l'override di compare(T o1, T o2) (che DEVE usare compareTo? non credo, potrei decidere io quando due oggetti sono uguali o uno minore dell'altro)
    in classi reali o anonime, ecc..

    DOMANDA: Quando usare "Comparable" e quando usare un "Comparator"?
    Teoricamente, basta il Comparator, ma se si usa ancora Comparable , in qualche caso si userà..... puoi farmi un esempio valido?

    Comparable, quando "se stesso" deve essere spesso confrontato con altri oggetti? e Comparator quando "esternamente", in un altro metodo ad esempio, si necessita confrontare due oggetti?

    Sto facendo un po' di casino.....
    diciamo che Comparable implementato da una classe, mi sembra di aver capito l'uso, il Comparator nelle List è OK, ma il Comparator come implementazione in una classe......che scopo ha?

    Il problema ora non è come si usa, ma "quando" si usa.

    Sempre grazie per la disponibilità
    Ciao
    Tagan
  • Re: Comparator e ordinamento per più campi

    tagan ha scritto:


    Come me la faccio l'esperienza in Java SE ?
    Realizzando (per conto tuo) applicazioni, utili o fittizie che siano .... e leggendo/studiando su libri specifici.

    tagan ha scritto:


    è possibile usarlo anche in ambiente EE.
    La piattaforma Java SE è la base per Java EE. Cioè, Java SE è il framework che ti ritrovi nel runtime Java, quindi una applicazione Java EE può usare tutte le classi di Java SE, dal I/O base alle collezioni alle regular expression, ecc..
    Poi è chiaro che in una applicazione Java EE è parecchio (altamente!) improbabile che usi AWT e/o Swing ma queste classi tecnicamente "ci sono".

    tagan ha scritto:


    Per quello che ho capito (non so se può esserti utile) stanno dismettendo JSP, (possibile?) parlano di fare l'html con PrimeFaces.
    JSP sono le pagine "dinamiche" base di Java EE. Poi ci sono le JSF ma è tutto un altro discorso perché è una specifica per fare interfacce utente nelle webapp basate su "componenti". Non è affatto improbabile, pure al giorno d'oggi, che tu magari debba fare poi per lavoro dello sviluppo/manutenzione su webapp che usano le JSP base ... e magari robaccia vecchia ... (a me era capitato ...).

    tagan ha scritto:


    Ma dovrei sapere anche cosa è un servlet che a oggi per me è solo un nome...... cmq, con calma...
    Sì, esatto .. con calma.

    tagan ha scritto:


    - Comparable è una interfaccia che necessita l'override di compareTo(T o)
    Sì, esatto

    tagan ha scritto:


    - Comparator è sempre una interfaccia e necessiata l'override di compare(T o1, T o2)
    Sì, esatto.

    tagan ha scritto:


    che DEVE usare compareTo?
    No, non è detto ... dipende da COSA compari. Se compari due stringhe, String è Comparable, quindi ha il compareTo (vedi quell'esempio linkato ieri).
    Se compari numeri hai diverse possibilità. Insomma, dipende ...

    tagan ha scritto:


    DOMANDA: Quando usare "Comparable" e quando usare un "Comparator"?
    Teoricamente, basta il Comparator, ma se si usa ancora Comparable , in qualche caso si userà..... puoi farmi un esempio valido?

    Comparable, quando "se stesso" deve essere spesso confrontato con altri oggetti? e Comparator quando "esternamente", in un altro metodo ad esempio, si necessita confrontare due oggetti?
    L'avevo spiegato proprio esattamente qui: https://www.iprogrammatori.it/forum-programmazione/java/comparator-t34643.html#p8598546
  • Re: Comparator e ordinamento per più campi

    andbin ha scritto:


    L'avevo spiegato proprio esattamente qui: https://www.iprogrammatori.it/forum-programmazione/java/comparator-t34643.html#p8598546
    Grazie.
    capito tutto.

    Stavo facendo un programmino con l'uso di ArrayList e poi usare i Comparator.
    nel metodo aggiungiOggetto() che sto implementando, ho usato
    
    if (!lista.contains(o))
    	lista.add(o);
    
    per far funzionare come si deve "contains", perché devo fare l'override del metodo equals() e per forza anche di hasCode()?

    oppure hai un link dove vedere l'implementazione di contains e capire perché servono entrambi? (vero che Eclipse te li genera lui e fai uno sforzo minimo, ma vorrei capire)

    Grazie.
    tagan
  • Re: Comparator e ordinamento per più campi

    tagan ha scritto:


    Stavo facendo un programmino con l'uso di ArrayList e poi usare i Comparator.
    nel metodo aggiungiOggetto() che sto implementando, ho usato
    
    if (!lista.contains(o))
    	lista.add(o);
    
    per far funzionare come si deve "contains", perché devo fare l'override del metodo equals() e per forza anche di hasCode()?
    contains() si basa sul concetto di "uguaglianza" espresso dal equals(). Ecco perché dovresti ridefinire appropriatamente equals() nella classe dei tuoi oggetti. Altrimenti resta il equals() di Object che si basa solo sulla "identità" e NON sul contenuto.

    hashCode() NON serve al contains(). Serve principalmente nelle collezioni basate su hash-table come HashMap o la "vecchia" Hashtable.
    Ma se ridefinisci equals() dovresti COMUNQUE ridefinire appropriatamente hashCode() perché c'è un "contratto" ben preciso tra questi due metodi.
    Poi certo ... se non usi mai i tuoi oggetti nelle collezioni che usano hashCode() ... vabbè, ok.

    tagan ha scritto:


    oppure hai un link dove vedere l'implementazione di contains e capire perché servono entrambi? (vero che Eclipse te li genera lui e fai uno sforzo minimo, ma vorrei capire)
    Il sorgente di contains() (e in generale delle classi di Java SE) un IDE dovrebbe potertelo mostrare, ammesso di avere nel IDE il collegamento corretto con i sorgenti del framework.

    Ma comunque non serve vedere la implementazione, già la documentazione javadoc di contains() descrive l'uso di equals().
Devi accedere o registrarti per scrivere nel forum
9 risposte