Java Collections Framework
Java Collections Framework
Pag. 1/19
Array
L'array l'unica collezione supportata internamente dal linguaggio Java, deriva da Object e implementa le interfacce Serializable e Clonable. A volte gli array sono chiamati comunemente anche vettori ma preferibile usare il termine array perch array e vettori in Java non sono esattamente la stessa cosa (lo vedremo in seguito). Un array, come molti gi sapranno, un insieme di oggetti tutti dello stesso tipo (cio omogenei tra di loro) distinguibili l'uno dall'altro in base a un indice o posizione. Ogni oggetto che fa parte dell'insieme detto elemento dell'array mentre il numero degli elementi dell'array (detto dimensione) una volta fissato non pu pi essere cambiato. Di solito gli elementi di un array vengono memorizzati in locazioni contigue di memoria. L'indice (intero che pu essere una costante o un'espressione numerica) che serve per accedere ad ogni singolo elemento deve essere un valore valido, cio compreso tra 0 e la dimensione dell'array 1, altrimenti il linguaggio genera una eccezione di tipo ArrayIndexOutOfBoundsException.
Fig. 2 Un array appena dichiarato (il pallino nero rappresenta il valore speciale null)
Possiamo dichiarare e creare l'array con l'operatore new: int[] a = new int[5]; In questo momento tutti gli elementi dell'array sono uguali a 0 (per un array di interi, uguali a spazi per un array di char, uguali a false per un array di boolean ecc).
ITIS Castelli, BS
Maurizio Cozzetto
Java
Pag. 2/19
ITIS Castelli, BS
Maurizio Cozzetto
Java
Pag. 3/19
Possiamo contestualmente dichiarare e inizializzare i valori dell'array in maniera rapida e diretta: int[] a = {2,4,6,8,10};
Arraydioggetti
Gli elementi di un array possono anche essere oggetti ad esempio stringhe. Ricordiamo che le stringhe di caratteri sono oggetti della classe String: String s = new String("Questa una stringa"); Tuttavia il modo pi comune di creare una stringa consiste nell'assegnare una costante stringa a un oggetto di tipo String: String s = "Questa una stringa"; Per creare in maniera rapida un array di stringhe, possiamo quindi scrivere: String[] winx = {Bloom,Stella,Musa,Flora,Tecna,Aisha};
Java
Pag. 4/19
GregorianCalendar dataDiNascita; String occhi; String statoAttuale; String abilita; String alleati; String nemici; String parenti; String base; public Fata(String nome, String luogoDiNascita, GregorianCalendar dataDiNascita, String occhi, String abilita) { this.luogoDiNascita = luogoDiNascita; this.dataDiNascita = dataDiNascita; this.nome = nome; this.occhi = occhi; this.abilita = abilita; } private String dateToString(GregorianCalendar dataDiNascita) { int anno = dataDiNascita.get(Calendar.YEAR); // i mesi si contano da zero, quindi JANUARY=0, FEBRUARY=1 ecc int mese = dataDiNascita.get(Calendar.MONTH)+1; int giorno = dataDiNascita.get(Calendar.DAY_OF_MONTH); String dataStr = giorno+"/"+mese+"/"+anno; return dataStr; } public String toString() { return "Nome "+nome+ ", luogo di nascita "+luogoDiNascita+", data di nascita "+dateToString(this.dataDiNascita)+", occhi "+occhi; } } // fine classe Fata
Fig. 8 Le Winx: da sinistra a destra: Flora, Aisha, Musa, Bloom, Tecna e Stella
Ora istanziamo 3 dei 6 oggetti corrispondenti ai nostri personaggi (ipotizziamo che il seguente codice sia contenuto nel metodo statico main di una classe di prova Main): Fata bloom = new Fata("Bloom", "Domino", new GregorianCalendar(1988, 11, 10),"Celesti", "Fiamma del drago"); Fata stella = new Fata("Stella", "Solaria", new GregorianCalendar(1987, 7, 18),"Marroni","Potere del sole e della luna");
ITIS Castelli, BS
Maurizio Cozzetto
Java
Pag. 5/19
Fata aisha = new Fata("Aisha", "Andros", new GregorianCalendar(1989, 4, 15), "Celesti-blu", "Fluido Morfix"); Creiamo ora l'array winxClub: Fata[] winxClub = new Fata[6]; e assegniamo i valori ai singoli elementi dell'array: winxClub[0] = bloom; winxClub[1] = stella; // Dobbiamo ancora istanziare le altre winx Musa, Flora e Tecna winxClub[5] = aisha; Avremo potuto scrivere il codice in maniera pi compatta come abbiamo visto precedentemente: Fata[] winxClub = {bloom, stella, null, null, null, aisha}; In questa situazione non tutti gli elementi sono assegnati: scorrendo quindi l'array in maniera sequenziale, dobbiamo escludere dal conteggio i reference uguali a null. for (int i = 0; i<winxClub.length; i++) if (winxClub[i]!=null) System.out.println(winxClub[i].toString());
ITIS Castelli, BS
Maurizio Cozzetto
Java
Pag. 6/19
CollectionsFramework
Una Collection una struttura dati - un oggetto in sostanza - che contiene riferimenti ad altri oggetti, di solito dello stesso tipo. Nelle prime versioni di Java in realt nelle Collection era possibile memorizzare oggetti di natura qualunque: per ricavare poi un particolare elemento della collezione era necessario effettuare un cast. Tipicamente le Collection rappresentano dati che formano gruppi naturali, come una mano di poker (Collection di carte), un elenco di brani su un cd (Collection di canzoni), una casella di posta elettronica (Collection di e-mail) o un elenco telefonico (Collection di coppie di tipo nomenumero di telefono). Un Collections Framework un'architettura unificata per rappresentare e manipolare le Collection. Tutti i Collections Framework contengono i seguenti elementi:
Interfacce: Nelle interfacce troviamo per definizione solo le operazioni ammissibili sulle collezioni (metodi astratti). Spetter poi alle classi concrete implementare i metodi descritti nelle interfacce. Implementazioni: Implementazioni concrete delle interfacce. In pratica, sono strutture dati riutilizzabili. Algoritmi: Metodi che eseguono operazioni utili, come la ricerca e lordinamento, allinterno di oggetti che implementano le interfacce. Gli algoritmi sono polimorfi, cio lo stesso metodo pu essere usato in molte differenti implementazioni di una certa interfaccia. In pratica, gli algoritmi sono funzionalit riutilizzabili.
Leinterfacce
Le Core Collections Interfaces descrivono differenti tipi di collezioni, come riportato in figura 10. Linterfaccia Collection la radice dell'albero (in Java un'interfaccia ne pu estendere un'altra) e comprende le interfacce List, Set e SortedSet, mentre Map la radice dell'interfaccia SortedMap. Alcuni tipi di collezioni permettono la duplicazione di elementi, altri non la permettono. Alcuni sono ordinati e altri non lo sono.
ITIS Castelli, BS
Maurizio Cozzetto
Java
Pag. 7/19
Diamo una breve descrizione delle interfacce: Set = Collezione che non pu contenere elementi duplicati. Linterfaccia fornisce unastrazione del modello matematico di insieme. List = Collezione ordinata (detta anche sequenza), che pu contenere elementi duplicati, ciascuno dei quali identificato da un indice (la posizione). Map = Oggetto che collega chiavi a valori. Una mappa non pu contenere chiavi duplicate e ogni chiave pu essere collegata al massimo con un valore. Modella il concetto matematico di funzione. SortedSet = E un Set che mantiene gli elementi in ordine crescente. Diverse operazioni aggiuntive sono fornite per usufruire dei vantaggi dellordinamento. SortedMap = E una Map che mantiene il collegamento chiave-valore in ordine crescente di chiave.
Leimplementazioni
Le implementazioni pi comunemente utilizzate sono quelle riportate in tabella 1 ma noi studieremo solo Set, List e Map. Ogni implementazione fornisce tutte le operazioni contenute nella sua interfaccia. Diamo una breve descrizione di alcune classi concrete: HashSet = Gli elementi sono memorizzati in ordine sparso, senza alcuna garanzia sullordine in cui potranno essere letti. ArrayList = Implementazione di List come array ridimensionabile ( possibile aggiungere ed eliminare elementi). Vector = E' come ArrayList ma i suoi elementi sono Object (inoltre la classe sincronizzata) HashMap = Implementazione di Map ma gli elementi non sono ordinati Interfaccia Set Implementazione HashSet TreeSet LinkedHashSet List Map ArrayList LinkedList HashMap TreeMap LinkedHashMap Tab. 1 Rapporto tra Interfacce e implementazioni Vector Stack HashTable Properties Strutture dati storiche
InterfacciaListesuaimplementazioneArrayList
Riscriviamo l'esempio precedente usando questa volta la classe ArrayList e il concetto di genericit (generics) introdotto per la prima volta in Java 5.
ITIS Castelli, BS
Maurizio Cozzetto
Java
Pag. 8/19
Consideriamo sempre la classe di supporto Fata dell'esempio precedente e il seguente codice: List<Fata> winxClub = new ArrayList<Fata>(); // dichiarazione e creazione di un ArrayList In questo momento la situazione in memoria la seguente:
Fig. 11 Situazione dopo aver creato l'oggetto winxClub; in questo momento non ci sono elementi nella struttura dati
Non ci sono elementi e la struttura vuota: System.out.println("il winxclub vuoto? "+winxClub.isEmpty()); Subito dopo istanziamo un oggetto e aggiungiamolo alla struttura: Fata bloom = new Fata("Bloom", "Domino", new GregorianCalendar(1988, 11, 10),"Celesti", "Fiamma del drago"); winxClub.add(bloom); // aggiungiamo Bloom al winxClub Questa la situazione in memoria:
ITIS Castelli, BS
Maurizio Cozzetto
Java
Pag. 9/19
Java
Pag. 10/19
Possiamo continuare ad aggiungere elementi in maniera dinamica alla struttura col metodo add() anche senza usare direttamente nomi di variabili istanza: winxClub.add(new Fata("Aisha", "Andros", new GregorianCalendar(1989, 4, 15), "Celesti-blu", "Fluido Morfix")); Nulla vieterebbe di aggiungere ancora una volta un elemento aggiunto precedentemente: winxClub.add(stella); // abbiamo un elemento duplicato E' possibile farsi dare il numero di elementi della struttura col metodo size() (size() tiene conto anche degli elementi duplicati): System.out.println(winxClub.size()); Osserviamo che il metodo add() restituisce un valore booleano true se l'inserimento ha successo, false in caso contrario: if(winxClub.add(bloom)==true) System.out.println(Inserimento effettuato); else System.out.println(Impossible inserire l'elemento); Possiamo scorrere la struttura con un oggetto di tipo Iterator for (Iterator i=winxClub.iterator(); i.hasNext(); ) System.out.println(i.next().toString()); oppure con un ciclo for generalizzato: for (Fata f : winxClub) System.out.println(f.toString()); oppure con un ciclo for con indice: for (int i=0; i<winxClub.size(); i++) System.out.println(winxClub.get(i).toString()); Possiamo eliminare un elemento (il primo che incontriamo se ce n' pi di uno) dalla struttura col metodo remove(): boolean b = winxClub.remove(stella); if (b) System.out.println(Eliminata stella dal winxClub); else System.out.println(Impossibile eliminare stella); Viene ovviamente decrementato il valore restituito da size(). Oppure possiamo passare al metodo remove() la posizione dell'elemento che intendiamo cancellare; la posizione deve essere un valore compreso tra 0 e size()-1, altrimenti il programma genera una eccezione di tipo IndexOutOfBoundsException. Questa volta il metodo restituisce un reference all'oggetto eliminato invece che un valore booleano: Fata f = winxClub.remove(2); if (f==null) System.out.println(Impossibile eliminare l'elemento di posizione 2); else System.out.println(Elemento eliminato +f.toString());
ITIS Castelli, BS
Maurizio Cozzetto
Java
Pag. 11/19
Creiamo ora una winx fittizia (elemento dummy) e aggiungiamola alla lista: Fata g = new Fata("Fata dummy","Luogo di nascita dummy", new GregorianCalendar(1988,1,1),"Colore dummy","Abilit dummy"); winxClub.add(g); Ora creiamo musa e sostituiamola all'elemento dummy col metodo set(): Fata musa = new Fata("Musa","Melody",new GregorianCalendar(1988,4,30),"Non noto","Potere della musica"); int d = winxClub.size(); winxClub.set(d-1,musa); Gli elementi dell'arraylist sono individuabili in base alla posizione; per accedere a un singolo elemento possiamo usare il metodo get(): Fata h = winxClub.get(2); if (h!=null) System.out.println(h.toString()); else System.out.println(Il reference null); Ci sarebbero molti altri metodi da analizzare ma non abbiamo tempo per studiarli tutti.
InterfacciaSetesuaimplementazioneHashSet
L'interfaccia Set contiene metodi ereditati da Collection ma aggiunge la limitazione che proibisce elementi duplicati. La piattaforma Java contiene 3 implementazioni generali di Set ma HashSet l'implementazione con le prestazioni migliori ma non abbiamo idea di quale sia lordine degli elementi quando si itera su di essi.
ITIS Castelli, BS
Maurizio Cozzetto
Java
Pag. 12/19
Nel linguaggio matematico e informatico, la funzione hash una funzione non iniettiva che mappa una stringa di lunghezza arbitraria in una stringa di lunghezza predefinita.
Le funzioni di hashing hanno una notevole importanza nel campo della crittografia, nell'ambito dei database e nella trasmissione dei dati. La funzione di hashing deve essere progettata in modo da ridurre al minimo le collisioni (si verifica una collisione quando due stringhe di input producono lo stesso valore di hash) e, anche qualora queste si verificassero, devono esistere delle politiche di gestione delle collisioni. Il nostro esempio questa volta (capiremo pi avanti perch) riguarda i supereroi della casa editrice Marvel (i Vendicatori, con le varianti New Avengers, Mighty Avengers, fig. 17 e fig. 18 rispettivamente). class SuperEroe { String nome; String editore; String alterEgo; String creatoDa; String primaApparizione; int altezza; // in cm int peso; // in kg String occhi; String capelli; String statoAttuale; String abilita; String alleati; String nemici; String parenti; public SuperEroe(String nome, String alterEgo, String abilita) { this.nome = nome; this.alterEgo = alterEgo; this.abilita = abilita; } public String toString() { return "nome: "+nome+", alter ego: "+alterEgo+", abilit: "+abilita; } } Creaimo ora l'HashSet nel seguente modo: Set<SuperEroe> newAvengers = new HashSet<SuperEroe>(); Non vi sono elementi nell'HashSet in questo momento: provvediamo quindi ad aggiungerne tre: SuperEroe spiderman = new SuperEroe("Spider-Man","Peter Parker","Tutte le capacit di un ragno moltiplicate alla taglia umana (forza, velocit, agilit), senso di ragno che lo avverte dei pericoli"); newAvengers.add(spiderman); SuperEroe wolverine = new SuperEroe("Wolverine","James Howlett (Logan)","fattore rigenerante, artigli protrattili, sensi sviluppatissimi, forza e agilit notevoli, esperto di arti marziali"); newAvengers.add(wolverine); SuperEroe ironMan = new SuperEroe("Iron Man","Antony Edward (Tony) Stark","Genio scientifico, capacit dell'armatura: superforza, semi-invulnerabilit, ITIS Castelli, BS Maurizio Cozzetto Brescia, 18 nov 2009
Java
Pag. 13/19
volo, raggi repulsori, armi da fuoco e missili di varia natura"); newAvengers.add(ironMan); Il codice precedente non dissimile da quello usato con le liste (il metodo add() infatti polimorfico) e la rappresentazione in memoria, come si pu vedere nella fig. 16, non neanche molto diversa, tuttavia con i set non esiste il concetto di posizione dell'elemento. Questo significa inoltre che l'esecuzione di un ciclo di lettura degli elementi come il seguente, se lanciato pi volte, pu produrre risultati differenti: // Visualizziamo tutti gli elementi for (SuperEroe se : newAvengers) System.out.println(se.toString());
ITIS Castelli, BS
Maurizio Cozzetto
Java
Pag. 14/19
Fig. 17 Copertina del primo numero The New Avengers (I nuovi vendicatori)
Bulkoperations
Le bulk operations (letteralmente operazioni in blocco) sono particolarmente utili se applicate allinterfaccia Set, perch permettono di applicare su insiemi le operazioni algebriche di unione (addAll()), differenza (removeAll()) e intersezione (retainAll()), oltre a permettere di verificare se un dato insieme sottoinsieme di un altro (containsAll()). Se s1 e s2 sono set, allora: s1.addAll(s2) Trasforma s1 nell'unione degli elementi di s1 ed s1 (coincide con l'unione insiemistica, cio trova l'insieme degli elementi di s1 ed s2). s1.removeAll(s2) Trasforma s1 nella differenza insiemistica (asimmetrica) di s1 ed s2 (cio calcola l'insieme degli elementi di s1 che non appartengono ad s2). s1.retainAll(s2) Trasforma s1 nell'intersezione di s1 ed s2 (cio il set contiene tutti gli elementi di s1 ed s2 che sono comuni). I metodi addAll(), removeAll() e retainAll() restituiscono true se s1 stata modificata in seguito allesecuzione delloperazione. s1.containsAll(s2) Restituisce true se s2 sottoinsieme di s1. Per calcolare l'unione, l'intersezione o la differenza insiemistica di due set in modo non distruttivo (senza modificare i set), l'oggetto chiamante deve copiare il set prima di chiamare l'operazione bulk. Per provare alcuni dei metodi sopra elencati, consideriamo il nuovo set relativo ai Potenti Vendicatori (The Mighty Avengers): Set<SuperEroe> mightyAvengers = new HashSet<SuperEroe>();
ITIS Castelli, BS
Maurizio Cozzetto
Java
Pag. 15/19
SuperEroe quickSilver = new SuperEroe("Quicksilver","Pietro Django Maximoff","Supervelocit"); SuperEroe stature = new SuperEroe("Stature","Cassandra Eleanor (Cassie) Lang","Pu diventare alta fino a 30 metri o piccola come una formica, pu emettere scariche di energia"); Aggiungiamo i 3 Potenti Vendicatori: mightyAvengers.add(quickSilver); mightyAvengers.add(stature); mightyAvengers.add(ironMan); Vogliamo l'intersezione tra newAvengers e mightyAvengers // Copia del set newAvengers Set<SuperEroe> intersezione = new HashSet<SuperEroe>(newAvengers); intersezione.retainAll(mightyAvengers); System.out.println("Intersezione tra NewAvengers e MightyAvengers"); for (SuperEroe se : intersezione) System.out.println(se.toString()); Il risultato, come prevedibile, ironMan. Lasciamo al lettore il compito di creare eventualmente altri gruppi e di provare le altre operazioni insiemistiche, magari facendo delle prove con i Dark Avengers (fig. 19), guardando su www.wikipedia.it o www.wikipedia.en.
InterfacciaMapesuaimplementazioneHashMap
Java contiene 3 implementazioni generali di Map mentre l'implementazione migliore HashMap che per non fornisce una garanzia sull'ordine degli elementi, come si pu intuire rileggendo la sezione relativa all'interfaccia Set. Per fare un esempio di HashMap, consideriamo come chiave di accesso agli elementi della mappa il nome del supereroe. Mappiamo cio i nomi (stringhe) con gli oggetti (di tipo SuperEroe). Istanziamo una mappa: Map<String, SuperEroe> newAvengers = new HashMap<String, SuperEroe>();
ITIS Castelli, BS
Maurizio Cozzetto
Java
Pag. 16/19
ITIS Castelli, BS
Maurizio Cozzetto
Java
Pag. 17/19
Per aggiungere elementi alla mappa, scriviamo per esempio: SuperEroe spiderman = new SuperEroe("Spider-Man","Peter Parker","Tutte le capacit di un ragno moltiplicate alla taglia umana (forza, velocit, agilit), senso di ragno che lo avverte dei pericoli"); // Aggiungiamo spiderman alla mappa newAvengers.put(spiderman.nome,spiderman); Questa volta usiamo il metodo put() che richiede due argomenti (una chiave e un valore). Possiamo aggiornare il valore degli attributi dell'oggetto mappato dalla chiave usando ancora il metodo put(): // Aggiorniamo le abilit di Spider-Man spiderman.abilita = "Forza, velocit, agilit, senso di ragno"; newAvengers.put(spiderman.nome,spiderman); // Controlliamo se la variazione stata effettuata System.out.println(spiderman.toString()); Aggiungiamo un nuovo vendicatore: SuperEroe wolverine = new SuperEroe("Wolverine","James Howlett (Logan)","Fattore rigenerante, artigli protrattili, sensi sviluppatissimi, forza e agilit notevoli, esperto di arti marziali"); newAvengers.put(wolverine.nome, wolverine);
Fig. 20 Rappresentazione di una mappa (nella figura sono state rappresentate le chiavi)
Il metodo values() restituisce la Collection dei valori: Collection<SuperEroe> elencoNewAvengers = newAvengers.values();
ITIS Castelli, BS
Maurizio Cozzetto
Pag. 18/19
Java
Pag. 19/19
Fig. 22 Spider-Man rivela al mondo la sua identit segreta (Civil War n. 1, miniserie Marvel)
Fontidiriferimento
Java Tutorial, Corso rapido quarta edizione, AV, McGraw-Hill Java Collection Framework, John Zuckowski, http://www.ibm.com/developerworks/edu/j-dw-javacoll-i.html Wikipedia (en e it), http://www.wikipedia.org Java Riassunto, Francesco Galgani, http://www.guella.it/universita/javariassunto.pdf Informatica Programmazione in Java, F. Scorzoni, Loescher
ITIS Castelli, BS
Maurizio Cozzetto