Conta per ogni elemento della lista

di il
36 risposte

Conta per ogni elemento della lista

Ragazzi ho un piccolo problema.

A scopo puramente didattico ho sviluppato un'applicazione che gestisce un'agenzia di viaggi.

Il viaggio ovviamente ha una località ed una data.

Ora vorrei che cliccando su un pulsande mi si aprisse una JDialog con una tabella dove vi siano presenti tutte le città visitate dai clienti e con il numero di viaggi effettuati in quella città.

Es.
Nome Città| Numero Visite|
Parigi|3|
Londra|2|
Berlini |5 |


come faccio? ho provato diversi metodi ma nessuno ha funzioanto.

36 Risposte

  • Re: Conta per ogni elemento della lista

    CRTVLB ha scritto:


    come faccio? ho provato diversi metodi ma nessuno ha funzioanto.
    In che senso? Quali sono i dubbi?
    Deduco che vuoi visualizzare una tabella con 2 colonne ... bene, come componente grafico, JTable è appropriato in questo caso. Il problema è come usare JTable?
    Ma il punto è anche un altro: come è fatta la "base di dati"? Hai usato un DBMS reale (es. MySQL o altro) o è tutto fittizio (e forse nemmeno persistente su disco) con strutture dati/collezioni in memoria?
    Questi aspetti li dovresti spiegare ... altrimenti non ti possiamo aiutare più di tanto.
  • Re: Conta per ogni elemento della lista

    Scusami, hai pienamente ragione.

    I dati sono sulla persistenza.

    in pratica ho una classe chiamata agenzia, li ho un metodo:
    
     public void aggiungiViaggio(Viaggio viaggio) {
            this.listaAttori.add(viaggio);
        }
    
    Gli attributi del viaggio sono data e luogo, nella persistenza ho diversi viaggi, ad esempio:
    
    Viaggio parigi1 = new Viaggio();
            parigi.setData(new GregorianCalendar(2012, 3, 11));
            parigi1.setLuogoViaggio("Parigi");
    
    Viaggio parigi2 = new Viaggio();
            parigi2.setData(new GregorianCalendar(2014, 5, 10));
            parigi2.setLuogoViaggio("Parigi");
    
    Viaggio berlino1 = new Viaggio();
            berlino1.setData(new GregorianCalendar(2015, 7, 7));
            berlino1.setLuogoViaggio("Berlino");
    
    agenzia.aggiungiViaggio(parigi1);
    agenzia.aggiungiViaggio(parigi2);
    agenzia.aggiungiViaggio(berlino1);
    
    e cosi via.

    la mia classe viaggio è fatta in questo modo
    
    public class Viaggio implements Comparable <viaggio> {
        
        private String luogoViaggio;
        private Calendar data = new GregorianCalendar();
        
        public Viaggio() {}
        
        public Viaggio(Calendar data, String luogoViaggio) {
            this.luogoViaggio = luogoViaggio;
            this.data = data;
            data.getTime();
            data.setLenient(false);
        }
        
        public String getLuogoViaggio() {
            return luogoViaggio;
        }
    
        public void setLuogoViaggio(String luogoViaggio) {
            this.luogoViaggio = luogoViaggio;
        }
    
        public Calendar getData() {
            return data;
        }
    
        public void setData(Calendar data) {
            this.data = data;
        }
        
        public String toStringData() {
            Date d = data.getTime();
            SimpleDateFormat giorno = new SimpleDateFormat("EEEE dd-MMMM-yyyy"); 
            return giorno.format(d);
        }
        
        @Override
        public String toString() {
            String stringa = "";
            stringa += "Luogo" + luogoViaggio +"\n";
            stringa += "Data " + data +"\n";
            return stringa;
        }
    
        public int compareTo(Avvistamento o) {
            return getData().compareTo(o.getData());
        }
    
    quello che vorrei fare io è premento un JBotton far apparire una JDialog con dentro una JTable dove vi compaiano tutte le città e quanti viaggi sono stati effettuati verso ogni località. Questo lo so fare, quello che non mi riesce è proprio il metodo che conta per ogni località quanti viaggi sono stati effettuati.

    Spero di essere stato più chiaro questa volta, se manca qualcosa chiedi pure.
  • Re: Conta per ogni elemento della lista

    CRTVLB ha scritto:


    quello che non mi riesce è proprio il metodo che conta per ogni località quanti viaggi sono stati effettuati.
    Quindi nella classe Agenzia hai un

    private List<Viaggio> listaAttori;

    giusto?

    Quindi dovrebbe essere (logicamente e per buon senso) Agenzia ad offrire un metodo "dammi il conteggio dei viaggi raggruppati per città".

    Dato che ogni città es. "Parigi" è in ciascun oggetto Viaggio, sei tu che devi implementare la logica di raggruppamento/conteggio. E non è difficile.

    Fai un metodo che usa internamente un:

    HashMap<String,Integer>

    dove la chiave è il nome della città e il valore è un "contatore".

    Iteri sulla listaAttori, per ciascun Viaggio prendi la città: è già nella mappa? Se sì incrementi il contatore, se no inserisci l'associazione della città con contatore a 1.

    Poi sei liberissimo di decidere di restituire:

    a) Direttamente il Map<String,Integer> (però non è ordinata, lo deve maneggiare il chiamante)

    oppure

    b) Qualcosa di meglio strutturato (e magari già ordinato per città) tipo un List<ViaggiCitta> dove ViaggiCitta è un banale bean con il nome città e il contatore.
  • Re: Conta per ogni elemento della lista

    Ok, grazie mille.

    Ci provo e poi ti faccio sapere se sono riuscito a fare quello che volevo.
  • Re: Conta per ogni elemento della lista

    Ci sto provando da un po' ma senza risultati.

    Potresti darmi qualche suggerimento? specialmente per quel che riguarda la seconda soluzione?
  • Re: Conta per ogni elemento della lista

    CRTVLB ha scritto:


    Potresti darmi qualche suggerimento? specialmente per quel che riguarda la seconda soluzione?
    La soluzione che ho indicato con b) si può basare sulla a) (quindi facendo comunque prima la mappa per velocizzare il raggruppamento/conteggio) oppure può essere gestita a sé stante con un solo List magari sfruttando la tecnica del "binary search" (che presuppone e mantiene la lista ordinata) per trovare più velocemente la città piuttosto che la banale ricerca "lineare".
    A te la scelta.

    P.S: la creazione/riempimento della mappa città-->n.viaggi non è per niente difficile .. meno di 8 righe di codice!

    P.S.2: mi vengono in mente due cose. Se vuoi puoi benissimo usare un TreeMap che mantiene ordinate le chiavi (città), però per un numero elevato di chiavi può risultare meno efficiente di HashMap.
    Inoltre se l'obiettivo finale è avere un List<ViaggiCitta> la mappa la puoi anche tipizzare <String, ViaggiCitta> che se ViaggiCitta ha un int, incrementare il conteggio viene anche più semplice.
    Come vedi le varianti sono diverse.
  • Re: Conta per ogni elemento della lista

    Ho provato a fare una cosa del genere ma non funziona, evidentemente non ho capito bene il funzionamento delle mappe

    public Map<String, List<Viaggio>> contaViaggiInCittà() {
    Map<String, List<Viaggio>> risultato = new HashMap<String, List<Viaggio>>();
    viaggio b = (viaggio)this.listaViaggi.get(0);
    viaggio c = (viaggio)this.listaViaggi.get(0);

    int a=0;
    for (Viaggio v : listaViaggi){
    b = (Viaggio) v;
    if(b.getLuogoViaggio().equals(c.getLuogoViaggio()))
    a = a+1;

    risultato.put(v.getLuogoViaggio(),a);
    }

    return risultato;

    Ovviamente mi da errore poiché a è un interno mentre la mappa ha come oggetti di ritorno un Object e una Stringa ed anche supponendo che a fosse una stringa non credo che funzionerebbe lo stesso.

    P.s. ma più o meno ci sono o sono completamente fuori strada?
  • Re: Conta per ogni elemento della lista

    CRTVLB ha scritto:


    P.s. ma più o meno ci sono o sono completamente fuori strada?
    Ehm .. parecchio fuori strada. Rileggi quello che dicevo:

    andbin ha scritto:


    Fai un metodo che usa internamente un:

    HashMap<String,Integer>

    dove la chiave è il nome della città e il valore è un "contatore".

    Iteri sulla listaAttori, per ciascun Viaggio prendi la città: è già nella mappa? Se sì incrementi il contatore, se no inserisci l'associazione della città con contatore a 1.
    Se poi proprio non riesci, dillo, ti mostro il codice basilare per ottenere un Map<String,Integer> (la mappa che associa il lungo al suo contatore dei viaggi)
  • Re: Conta per ogni elemento della lista

    andbin ha scritto:



    Se poi proprio non riesci, dillo, ti mostro il codice basilare per ottenere un Map<String,Integer> (la mappa che associa il lungo al suo contatore dei viaggi)
    Se puoi si, te ne sarei molto grato.
  • Re: Conta per ogni elemento della lista

    CRTVLB ha scritto:


    Se puoi si, te ne sarei molto grato.
    private List<Viaggio> listaViaggi;
    
      // .......
    
    public Map<String,Integer> getConteggioViaggiPerLuoghi() {
        Map<String,Integer> mappaConteggi = new HashMap<String,Integer>();
    
        for (Viaggio viaggio : listaViaggi) {
            String luogo = viaggio.getLuogoViaggio();
            Integer conteggio = mappaConteggi.get(luogo);
            mappaConteggi.put(luogo, conteggio == null ? 1 : conteggio+1);
        }
    
        return mappaConteggi;
    }
    Il codice non l'ho provato, l'ho scritto al volo ma dovrebbe essere giusto, salvo miei errori di digitazione.

    Il risultato è ovviamente una mappa non ordinata. Banalmente basterebbe istanziare un TreeMap (che ordina per le chiavi, quindi per i luoghi) ma TreeMap può risultare meno efficiente di HashMap.

    Comunque puoi ottenere un es. List<ConteggioViaggiPerLuogo> (ConteggioViaggiPerLuogo altra semplice classe "bean" con luogo e contatore viaggi) e magari ordinato, facendo qualche passaggio in più.

    Cerca di comprendere il senso del codice che ho scritto.
  • Re: Conta per ogni elemento della lista

    Grazie mille, ora mi è tutto più chiaro, c''è solo una cosa che non ho compreso bene:

    andbin ha scritto:


    
    
    mappaConteggi.put(luogo, conteggio == null ? 1 : conteggio+1);
    
    in particolare non capisco questo: ? 1 potresti spiegarmi il funzionamento?

    Edit: forse ho capito sostuisce il ciclo if ed else, giusto?
  • Re: Conta per ogni elemento della lista

    CRTVLB ha scritto:


    Edit: forse ho capito sostuisce il ciclo if ed else, giusto?
    Sì, esatto. È l'operatore "condizionale" (detto a volte "ternario" perché è l'unico con 3 operandi). Permette una sorta di if-else per due espressioni che devono dare un valore.

    Altrimenti avrei dovuto fare:
    if (conteggio == null) {
        mappaConteggi.put(luogo, 1);
    } else {
        mappaConteggi.put(luogo, conteggio+1);
    }
    Corretto ma più lungo e con il mappaConteggi.put evidentemente duplicato.
    Quindi l'operatore condizionale risulta utile e compatto pur mantenendo leggibilità.

    Nota che viene comunque coinvolto l'auto-boxing/unboxing di Java 5. Il 1 è un int e viene fatto il boxing automatico a Integer. In conteggio+1 c'è un oggetto coinvolto in una somma e il compilatore sa che è un valore intero, quindi fa un unboxing a int, esegue la somma e fa un boxing a Integer.
  • Re: Conta per ogni elemento della lista

    Ho un altro piccolo problema, come imposto il modelloTabella della tabella che visualizza i risultati della ricerca?

    Io avevo pensato ad una cosa del genere:
    
    public class ModelloTabellaContaViaggi extends AbstractTableModel {
        
        private Agenzia agenzia;
        
        private Logger logger = (Logger) LoggerFactory.getLogger(ModelloTabellaContaViaggi.class);
    
        public  ModelloTabellaContaViaggi(Agenzia agenzia) {
            this.agenzia = agenzia ;
            logger.info("\nCaricamento Dati Tabella Avvenuto Con Successo" + this.getClass().getName() +"\n");
        }
    
        public int getRowCount() {
            // cosa metto qui?
        }
    
        public int getColumnCount() {
            return 2;
        }
    
        public Object getValueAt(int r, int c) {
           if(c == 0) {
               return this.agenzia.getListaViaggi().get(r).getLuogoViaggio();
           }else if (c == 1) {
               // qui invece cosa metto?
           }
           return null;
        }
        
        @Override
        public String getColumnName(int c) {
            if(c == 0) {
               return "Luogo Viaggio";
           }else if (c == 1) {
               return "Numero di viaggi";
           }
           return null;
            
        }
    
  • Re: Conta per ogni elemento della lista

    CRTVLB ha scritto:


    Ho un altro piccolo problema, come imposto il modelloTabella della tabella che visualizza i risultati della ricerca?

    Io avevo pensato ad una cosa del genere
    No, mi spiace, non va molto bene, per diversi motivi.

    Innanzitutto non serve a molto (almeno non senza altre valutazioni) che il model abbia direttamente il riferimento all'oggetto Agenzia. Serve invece che il model abbia il riferimento alla lista dei luoghi con conteggio. Non so come l'hai chiamata, nel mio post precedente la indicavo con List<ConteggioViaggiPerLuogo>

    Inoltre non va bene che nel getValueAt venga fatto

    this.agenzia.getListaViaggi().get(r).getLuogoViaggio()

    Il getListaViaggi è il metodo che fa tutta quella elaborazione (come ti ho mostrato prima), e siccome il getValueAt può essere invocato tantissime volte (specialmente se continui a scrollare avanti e indietro con tanti record), allora può diventare inefficiente.

    Quindi suggerisco (molto abbozzato!!):
    public class ModelloTabellaContaViaggi extends AbstractTableModel {
        private List<ConteggioViaggiPerLuogo> listaConteggioViaggiPerLuogo;
    
        public ModelloTabellaContaViaggi(List<ConteggioViaggiPerLuogo> listaConteggioViaggiPerLuogo) {
            this.listaConteggioViaggiPerLuogo = listaConteggioViaggiPerLuogo;
        }
    
        // .... getRowCount(), getColumnCount(), getColumnName, ecc....
    
        public Object getValueAt(int row, int column) {
            ConteggioViaggiPerLuogo conteggioViaggiPerLuogo = listaConteggioViaggiPerLuogo.get(row);
    
            if (column == 0) {
                return conteggioViaggiPerLuogo.getLuogoViaggio();
            } else if (column == 1) {
                return conteggioViaggiPerLuogo.getNumeroViaggi();
            }
    
            return null;  // default (non "dovrebbe" capitare se il model è implementato correttamente ed è usato solo da JTable)
        }
    }
    Ovviamente ho ipotizzato una classe ConteggioViaggiPerLuogo con almeno getLuogoViaggio()/getNumeroViaggi().
Devi accedere o registrarti per scrivere nel forum
36 risposte