Strutture: Mappa (o dizionario)

di il
18 risposte

Strutture: Mappa (o dizionario)

Salve,
ho 3 file, uno contiene un elenco di magazzini, un altro un elenco di prodotti e un altro associa i prodotti ai magazzini specificandone la quantità.

Per risolvere il problema ho creato una mappa che associa magazzino e prodotto che diventerà poi argomento di una nuova mappa che associa Mappa e quantità.

È una buona soluzione per risolvere il problema?

GernicMapManager.java

package com_antomau_WareHouseManagement.GenericObjects;


import java.util.Map;
import java.util.HashMap;


import com_antomau_WareHouseManagement.MyObjects.MyWarehouse;
import com_antomau_WareHouseManagement.MyObjects.MyProduct;



public class GenericMapManager {
	
	
	
	
	//variabili
	public static Map<MyWarehouse, MyProduct> map; //nuova mappa
	public static Map<Map<MyWarehouse, MyProduct>, Integer> finalMap;
	
	
	
	/**
	 * costruttore della classe
	 */
	public GenericMapManager()
	{
		map = null; //inizializzazione della mappa al tipo HashMap
		finalMap = new HashMap<Map<MyWarehouse, MyProduct>, Integer>();
	}
	
	
	
	
	
	public void addToMap(MyWarehouse w, MyProduct p, int i)
	{
		map = new HashMap<MyWarehouse, MyProduct>(); //inizializzazione della mappa al tipo HashMap
		map.put(w, p);
		finalMap.put(map, i);
	}
	
	
	
	
}


MyMapManager.java

package com_antomau_WareHouseManagement.MyObjects;



import com_antomau_WareHouseManagement.GenericObjects.GenericMapManager;





import java.util.Set;
import java.util.Map;



public class MyMapManager extends GenericMapManager {
	
	//l'oggetto di tipo mappa si chiama 'map' all'interno della superclasse
	
	/**
	 * costruttore della classe
	 */
	public MyMapManager()
	{
		super(); //invocazione del metodo costruttore
	}
	
	
	
	
	
	
	
	
	//ottenere i dati della mappa
	
	
	
	
	public void getData()
	{
		/*
		 * devo ottenere la chiave della mappa generale
		 * la chiave della mappa generale e' la mappa specifica
		 * il valore della mappa specifica e' il numero di prodotti nel magazzino
		 * 
		 * devo ottenere la chiave della mappa specifica
		 * la chive della mappa specifica e' il magazzino
		 * il valore della mappa specifica e' il prodotto
		 */
		
		
		//variabili
		int qt=0;
		MyWarehouse w = null;
		MyProduct p = null;
		
		
		//ottengo il keyset della mappa generale
		Set<Map<MyWarehouse, MyProduct>> finalSet = finalMap.keySet();
		
		//itero per ogni chiave della mappa generale
		for (Map<MyWarehouse, MyProduct> m : finalSet)
		{
			
			//ottengo il numero di prodotto per questo magazzino
			//ottengo il valore della corrente chiave della mappa
			qt = finalMap.get(m); //ottengo il valore della chiave m della mappa finalMap
			
			//ottenere il keySet della mappa interna (m)
			Set<MyWarehouse> mapSet = m.keySet();
			
			//itero per ogni chiave della mappa interna
			for (MyWarehouse iw : mapSet)
			{
				
				w = iw; //ottengo la chiave della mappa interna
				
				//ottengo il valore della chiave della mappa interna (m)
				
				p = m.get(w);
				
				
				//stampo i dati raccolti
				System.out.println(w.getSerial() + " <---> " + p.getSerial() + " <---> " + qt);
				
			}
			
		}
		
		
		
		
		
	} //end of getData()
	
	
	
	
	
	
	
	
	
	/**
	 * Questo metodo crea un set della mappa e lo restituisce
	 * @return Un set della mappa
	 */
	public Set<Map<MyWarehouse, MyProduct>> getSet()
	{
		Set<Map<MyWarehouse, MyProduct>> mySet = finalMap.keySet();
		
		return mySet;
	}
	
	
	
	
	
	
	/**
	 * @return Una stringa del contenuto della mappa da usare come testo di una JLabel formattata in HTML
	 */
	public String toLabel()
	{
		String result="";
		
		int qt; //quantità di prodotti nel tale magazzino
		MyWarehouse w; //il magazzino corrente
		MyProduct p; //il prodotto corrente
		
		//ottengo il set della mappa
		Set<Map<MyWarehouse, MyProduct>> finalSet = getSet();
		
		
		
		result += "<html><table border=\"1\">";
		
		
		
		/*
		 * iterazione della mappa e ottenimento delle varie chiavi
		 */
		for (Map<MyWarehouse, MyProduct> m : finalSet)
		{
			
			//ottengo il numero di prodotto per questo magazzino
			//ottengo il valore della corrente chiave della mappa
			qt = finalMap.get(m); //ottengo il valore della chiave m della mappa finalMap
			
			//ottenere il keySet della mappa interna (m)
			Set<MyWarehouse> mapSet = m.keySet();
			
			//itero per ogni chiave della mappa interna
			for (MyWarehouse iw : mapSet)
			{
				
				w = iw; //ottengo la chiave della mappa interna
				
				//ottengo il valore della chiave della mappa interna (m)
				
				p = m.get(w);
				
				
				//stampo i dati raccolti
				System.out.println(w.getSerial() + " <---> " + p.getSerial() + " <---> " + qt);
				
				result += "<tr><td>" + w.getSerial() + "</td><td>" + w.getAddress() + "</td>";
				result += "<td>" + w.getCity() + "</td><td>" + w.getCountry() + "</td><td>" + w.getPhone() + "</td<td>" + w.getFax() + "</td></tr>";
				
				result += "<tr><td>" + p.getSerial() + "</td><td>" + p.getDescription() + "</td><td>" + p.getPrice() +"</td><td>" + qt + "</tr>";
				
			}
			
		}
		
		
		
		
		
		result +="</table></html>";
		
		return result;
	}
	
	
	
	
	
	
	
	/*
	 * filtro di ricerca per la ricerca di un oggetto
	 */
	/**
	 * filtro di ricerca per un prodotto
	 * @param f il codice seriale del prodotto da cercare
	 * @return una stringa con i risultati formattata in html al file di essere impostata come argomento di una JLabel
	 */
	public String finder(String f)
	{
		String result="";
		
		
		
		
		int qt; //quantità di prodotti nel tale magazzino
		MyWarehouse w; //il magazzino corrente
		MyProduct p; //il prodotto corrente
		
		//ottengo il set della mappa
		Set<Map<MyWarehouse, MyProduct>> finalSet = getSet();
		
		
		
		result += "<html><table border=\"1\">";
		
		
		/*
		 * iterazione della mappa e ottenimento delle varie chiavi
		 */
		for (Map<MyWarehouse, MyProduct> m : finalSet)
		{
			
			//ottengo il numero di prodotto per questo magazzino
			//ottengo il valore della corrente chiave della mappa
			qt = finalMap.get(m); //ottengo il valore della chiave m della mappa finalMap
			
			//ottenere il keySet della mappa interna (m)
			Set<MyWarehouse> mapSet = m.keySet();
			
			//itero per ogni chiave della mappa interna
			for (MyWarehouse iw : mapSet)
			{
				
				w = iw; //ottengo la chiave della mappa interna
				
				//ottengo il valore della chiave della mappa interna (m)
				
				p = m.get(w);
				
				
				if (p.getSerial().equals(f))
				{
					result += "<tr></tr>"; //blank row
					result += "<tr><td>" + qt + "</td></tr>";
					result += "<tr><td>" + p.getSerial() + "</td><td>" + p.getDescription() + "</td><td>" + p.getPrice() + "</td></tr>";
					result += "<tr><td>Are stored in:</td></tr>";
					result += "<tr><td>" + w.getSerial() + "</td><td>" + w.getAddress() + "</td>";
					result += "<td>" + w.getCity() + "</td><td>" + w.getCountry() + "</td><td>" + w.getPhone() + "</td<td>" + w.getFax() + "</td></tr>";
				}
				
				
			}
			
		}
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		result +="</table></html>";
		
		
		
		
		
		return result;
		
		
	} //end of finder
	



Inoltre,
il metodo finder() può essere considerato un filtro di ricerca?

Progetto completo: https://github.com/myblacksloth/javaPrograms/tree/master/WarehouseManagement

18 Risposte

  • Re: Strutture: Mappa (o dizionario)

    antomau96 ha scritto:


    
    	public static Map<MyWarehouse, MyProduct> map; //nuova mappa
    	public static Map<Map<MyWarehouse, MyProduct>, Integer> finalMap;
    
    Le mappe concrete sono degli HashMap. E quindi la classe, i cui oggetti fanno da "chiave", DEVE avere delle caratteristiche ben precise. E da quanto vedo, la classe MyWarehouse NON mi risulta che abbia tali caratteristiche (quindi, morale: non ti funzionerà un bel niente o perlomeno avrai comportamenti "strani" e inaspettati).

    Inoltre avere una mappa come "chiave" in una mappa è qualcosa di abbastanza strano ... perlomeno molto inusuale.
  • Re: Strutture: Mappa (o dizionario)

    Il bello è che funziona perfettamente sia la stampa che la ricerca e quindi si riesce bene ad accedere ai vari elementi ma il problema è proprio che la mappa come argomento di una mappa non mi suona tanto di cosa corretta e quindi vorrei de consigli sul da farsi
  • Re: Strutture: Mappa (o dizionario)

    antomau96 ha scritto:


    Il bello è che funziona perfettamente sia la stampa che la ricerca e quindi si riesce bene ad accedere ai vari elementi
    Una classe, i cui oggetti vanno usati come chiavi in HashMap/Hashtable, DEVE ridefinire correttamente i metodi equals e hashCode, rispettando il "contratto" che esiste tra questi due metodi. La tua MyWarehouse ha questi requisiti? No da quanto vedo.

    Inoltre gli oggetti sarebbe bene che siano "immutabili", almeno per la parte di "stato" dell'oggetto che è coinvolta in equals/hashCode.

    E se come è adesso "ti funziona" .... allora ti funziona per puro c... ehm caso o comunque perché ne hai fatto un uso basilare o senza andare troppo oltre.

    antomau96 ha scritto:


    quindi vorrei de consigli sul da farsi
    Hai bisogno di più (molto di più) studio su Java. E anche su generics/collections.

    --> (ma servono comunque altri libri prima di questo)
  • Re: Strutture: Mappa (o dizionario)

    E se utilizzassi un oggetto Comparable tipizzato? Ad esempio Comparable<MyWarehouse> ? Sarebbe sbagliato anche in quel caso?
  • Re: Strutture: Mappa (o dizionario)

    Scusa ma posso sapere perché intendi che funziona per puro caso?
    Perché il codice hash per calcolare la posizione dell'oggetto viene ottenuto per puro caso?

    Oppure perché secondo te non può funzionare il sistema così implementato?

    Quello che ho fatto è stato
    creare una mappa <magazzino, prodotto>

    poi creare una mappa <mappa precedente, intero>

    e per ottenere i dati
    ottengo il keyset della mappa generale
    dalla chiave (mappa<mappa prec., int>) ottengo il numero di prodotti,
    poi ottengo il keyset della chiave (mappa interna)
    dalla quale vado a prendere il magazzino (chiave della mappa interna)
    e il prodotto (valore della mappa interna)....

    A realizzarlo è un pò macchinoso ma alla fine non credo che il ragionamento sia errato... forse è sbagliato creare un qualcosa del genere in Java....

    Adesso sto realizzando un altro programma molto simile
    https://github.com/myblacksloth/javaPrograms/tree/master/HotelManagement
    Per dispositivi mobili:
    https://github.com/myblacksloth/javaPrograms/blob/master/HotelManagement/README.md

    dove invece di usare la tecnica precedentemente descritta,
    ho creato una variabile in modo che il valore dell'associazione <chiave, valore> contenga la quantità di tale "oggetto valore" che corrisponde alla "chiave"

    Per capire meglio invece di fare:
    <<magazzino, prodotto>, numero>
    ho fatto
    <hotel, stanza> dove stanza già contiene la variabile numero che sarebbe il numero di stanze che l'hotel contiene...

    Però non credo che sia la scelta migliore perché così facendo vado a compromettere l'oggetto con una variabile che in realtà non lo dovrebbe caratterizzare....

    In Uni è stato consigliato di creare una lista per le stanze e una stuttura qualsiasi per gli hotel ma mi è già stato detto su questo forum che non è la strada migliore da percorrere pertanto vorrei evitare di commettere qualche castroneria che funziona bene ma è male implementata perché sarebbe come sparare una mosca col cannone...
  • Re: Strutture: Mappa (o dizionario)

    antomau96 ha scritto:


    E se utilizzassi un oggetto Comparable tipizzato? Ad esempio Comparable<MyWarehouse> ? Sarebbe sbagliato anche in quel caso?
    Comparable non c'entra nulla (e non ha alcun coinvolgimento) con HashMap. Al massimo ha a che fare con TreeMap. Ma HashMap e TreeMap sono due collezioni "mappe" con caratteristiche e soprattutto "prestazioni" ben differenti.
    Se usare una o l'altra dipende da vari fattori, che presumo non hai ancora approfondito.

    antomau96 ha scritto:


    Scusa ma posso sapere perché intendi che funziona per puro caso?
    Perché il codice hash per calcolare la posizione dell'oggetto viene ottenuto per puro caso?
    Per "caso" nel senso che ti spiegherò ora.

    Se non si ridefiniscono equals/hashCode, restano quelli "ereditati" da Object, che si basano solo sulla IDENTITÀ degli oggetti. Se in HashMap metti come chiavi oggetti che hanno equals/hashCode di Object, il valore associato alla chiave lo puoi ritrovare con get() solo e ripeto SOLO se hai in mano esattamente quel medesimo oggetto che avevi usato per il put().
    import java.util.HashMap;
    
    public class Prova {
        public static void main(String[] args) {
            HashMap<Persona,String> map = new HashMap<Persona,String>();
    
            Persona p1 = new Persona("Mario", "Rossi");
            map.put(p1, "studente");
    
            System.out.println(map.get(p1));  // Stampa: studente (ritrova la chiave nella map)
    
            Persona p2 = new Persona("Mario", "Rossi");
            System.out.println(map.get(p2));  // Stampa: null  (NON ritrova la chiave nella map)
        }
    }
    
    
    class Persona {
        private String nome;
        private String cognome;
    
        public Persona(String nome, String cognome) {
            this.nome = nome;
            this.cognome = cognome;
        }
    
        public String getNome() {
            return nome;
        }
    
        public String getCognome() {
            return cognome;
        }
    }
    Osserva bene il codice. p1 è usato sia per il put che per il primo get. Visto che l'oggetto è sempre quello, allora FUNZIONA. Quando fai il primo get, siccome hai in mano lo stesso oggetto "chiave" che c'è già dentro la map, allora si riesce ad ottenere il valore "studente".

    Poi creo un nuovo oggetto Persona che, guarda caso, ha lo stesso contenuto di p1. Ma il get con p2 NON funziona. Non avendo più quello stesso oggetto che c'è dentro la map, non si riesce a rintracciare la chiave e quindi nemmeno il valore. Anche se i due oggetti p1 e p2 hanno lo stesso contenuto.

    E tutto questo succede NON perché c'è qualcosa di sbagliato nella implementazione di HashMap ma perché la classe Persona NON ha ridefinito equals/hashCode e quindi resta il principio di uguaglianza che si basa SOLO sulla "identità" degli oggetti.

    Ed è questo che NON VA BENE in generale. Ci possono essere degli scenari in cui serve la mappatura delle chiavi per "identità" degli oggetti ma sono scenari ultra-particolari.

    antomau96 ha scritto:


    ottengo il keyset della mappa generale
    dalla chiave (mappa<mappa prec., int>) ottengo il numero di prodotti,
    poi ottengo il keyset della chiave (mappa interna)
    dalla quale vado a prendere il magazzino (chiave della mappa interna)
    e il prodotto (valore della mappa interna)....
    Se fai una iterazione sulle chiavi, ovviamente ottieni esattamente quegli oggetti che fanno da chiave e quindi se fai un get ovviamente FUNZIONA, pur basandosi sulla identità degli oggetti.

    Idem se per caso ti sei tenuto delle chiavi memorizzate da qualche parte (altra collezione, altre variabili). Finché fai così, "per caso" ti funziona, pur senza ridefinire equals/hashCode.

    Ma attenzione: NON è questo il modo giusto di ragionare con le mappe basate su hash-table interna (HashMap, Hashtable)

    Tutto questo ti è chiaro? Se sì, puoi andare (un po') avanti. Se no, è bene che (ri)studi un pochino.
  • Re: Strutture: Mappa (o dizionario)

    Si, mi è chiaro tutto perfettamente...

    Il problema è che nel mio caso io devo cercare la stringa di un codice seriale che deve essere per forza identica a quella dell'oggetto e quindi per questo non mi sono preoccupato tanto di questi due metodi... ma se sono fondamentali cercherò di implementarli nel programma che sto creando adesso...

    Ti volevo chiedere una cosa:
    nel nuovo programma che, come detto sopra è molto simile a questo, devo inserire i dati da file...
    Faccio come in questo caso che leggo i file nel main e inserisco i dati o è meglio passare i file alla classe che gestisce la struttura e si occupa lei dell'inserimento?

    es.: passo alla classe file1 e file2 e questa classe implementa gli scanner e i cicli while annidati per scansionare i dati oppure faccio tutto nel metodo main?

    Secondo me visto che ogni classe ha una responsabilità diversa conviene che la classe che gestisce la struttura faccia ciò...
    Cosa mi consigli?



    P.S.:
    Come metodo hashCode() questo va bene
    
     /**
         * @return the object's hash code
         */
        public int hashCode()
        {
            int code=0;
    
            int mult = 320;
            int add= 25;
            int div = 100;
            int mod = 36;
    
            code += type + num + price;
            code *= mult;
            code += add;
            code /= div;
            code %= mod;
    
    
            return code;
        }
    
    ?

    P.P.S.:
    E questo va bene come metodo equals()
    
      public boolean equals(MyRoom mr)
        {
            return ((super.type==mr.getType()) && (super.num==mr.getNum()) && (super.price==mr.getPrice()) && (tv==mr.getTv()) && (airConditioner==mr.getAirConditioner()) && fridge==mr.getFridge() && internet==mr.getInternet());
        }
    
    ?
  • Re: Strutture: Mappa (o dizionario)

    antomau96 ha scritto:


    es.: passo alla classe file1 e file2 e questa classe implementa gli scanner e i cicli while annidati per scansionare i dati oppure faccio tutto nel metodo main?

    Secondo me visto che ogni classe ha una responsabilità diversa conviene che la classe che gestisce la struttura faccia ciò...
    Cosa mi consigli?
    Se riesci a separare bene i concetti .. le responsabilità, beh, è meglio. Ma per arrivare a fare bene tutto questo, serve qualcosa che non si può semplicemente "studiare" ... serve esperienza.

    antomau96 ha scritto:


    Come metodo hashCode() questo va bene
    
     /**
         * @return the object's hash code
         */
        public int hashCode()
        {
            int code=0;
    
            int mult = 320;
            int add= 25;
            int div = 100;
            int mod = 36;
    
            code += type + num + price;
            code *= mult;
            code += add;
            code /= div;
            code %= mod;
    
    
            return code;
        }
    
    ?
    Contorto/macchinoso. E comunque bisogna anche vedere equals(). Entrambi devono essere implementati per rispettare il "contratto" che esiste tra i due metodi. Domanda: sai di che cosa si tratta?

    Per il momento, equals/hashCode falli generare dal IDE.
  • Re: Strutture: Mappa (o dizionario)

    antomau96 ha scritto:


    E questo va bene come metodo equals()
    
      public boolean equals(MyRoom mr)
        {
            return ((super.type==mr.getType()) && (super.num==mr.getNum()) && (super.price==mr.getPrice()) && (tv==mr.getTv()) && (airConditioner==mr.getAirConditioner()) && fridge==mr.getFridge() && internet==mr.getInternet());
        }
    
    ?
    NO, questo NON è un "override" ... è un overload. E quindi non funzionerebbe (se non sei tu ad invocarlo esplicitamente).

    Conosci override vs overload? Sono concetti di BASE importantissimi!
  • Re: Strutture: Mappa (o dizionario)

    Si, la differenza mi è chiara infatti mi rendo conto dell'errore....
    Per quanto riguarda le classi
    GenericRoom e MyRoom ho applicato l'override in questo modo
    
    /**
         * @return the object's hash code
         */
        @Override
        public int hashCode()
        {
            int code=0;
    
            int mult = 320;
            int add= 25;
            int div = 100;
            int mod = 36;
    
            code += type + num + price;
            code *= mult;
            code += add;
            code /= div;
            code %= mod;
    
            if (tv) code+=3;
            if(airConditioner) code+=5;
            if(fridge) code+=7;
            if(internet) code+=9;
    
    
            return code;
        }
    
    In quel caso non so come far combaciare i parametri.... perché uno è una classe generica e l'altro una classe specifica e quindi gli oggetti hanno parametri diversi...
  • Re: Strutture: Mappa (o dizionario)

    I metodi equals/hashCode falli GENERARE DAL IDE.
    Non hai ancora nozioni sufficienti per scriverli tu "bene".
  • Re: Strutture: Mappa (o dizionario)

    Scusa ma non mi sembrano eccezionalmente diversi dai miei... semplicemente ha implementato il codice della classe MyRoom diversamente da come avevo fatto io
    
    @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            if (!super.equals(o)) return false;
    
            MyRoom myRoom = (MyRoom) o;
    
            if (tv != myRoom.tv) return false;
            if (airConditioner != myRoom.airConditioner) return false;
            if (fridge != myRoom.fridge) return false;
            return internet == myRoom.internet;
        }
    
        @Override
        public int hashCode() {
            int result = super.hashCode();
            result = 31 * result + (tv ? 1 : 0);
            result = 31 * result + (airConditioner ? 1 : 0);
            result = 31 * result + (fridge ? 1 : 0);
            result = 31 * result + (internet ? 1 : 0);
            return result;
        }
    
    però perché mi imposta i metodi come Override anche nella superclasse?
    Perché si considera che essendo questa una sottoclasse di Object deve sovrascrivere i metodi presenti in tale classe?
  • Re: Strutture: Mappa (o dizionario)

    antomau96 ha scritto:


    Scusa ma non mi sembrano eccezionalmente diversi dai miei...
    Ma sono corretti E appropriati. Non come il tuo ... mult ... add ... div ... ma che, scherziamo?

    antomau96 ha scritto:


    però perché mi imposta i metodi come Override anche nella superclasse?
    Perché sono comunque "override" di quelli in Object.
  • Re: Strutture: Mappa (o dizionario)

    Ma sono corretti E appropriati. Non come il tuo ... mult ... add ... div ... ma che, scherziamo?
    Mi riferivo al procedimento che viene svolto... non all'implementazione che so di aver fatto in maniera ecccessivamente artigianale...


    Perché quando vado ad eseguire questo metodo
    
        /**
         * questo metodo aggiunge associazioni all'interno della mappa
         * @param h hotel da aggiungere alla mappa (key)
         * @param r stanza da aggiungere alla mappa (value)
         */
        public void addToMap(MyHotel h, MyRoom r)
        {
            theMap.put(h, r);
        }
    
    alla chiave h vengono soprascritte le associazioni e resta solo l'ultimo elemento associato?

    Ho tracciato i dati mediante il debugger e ho avuto modo di vedere che il dato si cancella
    hotel1 -> stanza1
    viene sovrascritto da
    hotel1 > stanza2
    che poi viene sovrascritto da
    hotel1 -> stanza3

    e in effetti quando vado a debuggare nella mappa c'è solo una voce...
    Devo associare all'hotel una nuova struttura (array o arrayList) che contiene le stanze?
Devi accedere o registrarti per scrivere nel forum
18 risposte