Collezioni in Java Collection Framework JFC • Java Collection Framework (JCF) fornisce il supporto a qualunque tipo di struttura dati, nel quadro di un'architettura logica globale e uniforme – interfacce che definiscono TIPI DI STRUTTURE DATI e i necessari concetti di supporto (es.: iteratori) – una classe Collections che definisce algoritmi polimorfi sotto forma di funzioni statiche, nonché servizi e costanti di uso generale; – classi che forniscono implementazioni dei vari tipi di strutture dati specificati dalle interfacce. • Obiettivo: strutture dati per "elementi generici" TRATTAMENTO DEI TIPI PRIMITIVI Interfacce fondamentali : •Collection: nessuna ipotesi sul tipo di collezione •Set: introduce l’idea di insieme di elementi (quindi, senza duplicati) •List: introduce l’idea di sequenza •SortedSet: l'insieme ordinato •Map: introduce l’idea di mappa, ossia tabella che associa chiavi a valori •SortedMap: una mappa (tabella) ordinata L'INTERFACCIA Collection Collection introduce l'idea di collezione di elementi • non si fanno ipotesi sulla natura di tale collezione – in particolare, non si dice che sia un insieme o una sequenza, né che ci sia o meno un ordinamento,.. etc • perciò, l'interfaccia di accesso è volutamente generale e prevede metodi per : Implementazioni delle interfacce Collection ArrayList Abbiamo detto che gli array non possono cambiare la propria dimensione: il numero di elementi contenuti viene stabilito al momento della creazione e rimane immutato. ! Per superare questa limitazione Java mette a disposizione la classe ArrayList, contenuta nel package java.util che permette di rappresentare sequenze di oggetti di lunghezza variabile. ! Ciascun oggetto in un’istanza di ArrayList viene identificato da un numero intero, detto indice, che ne indica la posizione. ! L'accesso ad una posizione inesistente provoca un errore (viene lanciata un'eccezione). Dichiarazione Es: ArrayList nomi= new ArrayList(); Il tipo ArrayList specifica un vettore di stringhe Alcuni metodi dell'ArrayList •Bisogna usare il metodo add per aggiungere elementi nel vettore • Es: nomi.add(“Marco”); aggiunge Marco al vettore nomi che ora ha dimensione 1 •Per ispezionare il valore di un elemento si usa il metodo get • Es: nomi.get(0); restituisce l’elemento avente indice 0 •Per assegnare un nuovo valore a un elemento di un vettore si usa il metodo set • Es: nomi.set(0,”Giovanni”); sostituisce l’elemento avente indice 0 con la stringa “Giovanni” • Si può anche inserire un elemento in una data posizione intermedia al’interno di un vettore: •Es: nomi.add(0,”Laura”) sposta il primo elemeto al secondo elemento e mette a primo elemento la stringa “Laura” •Al contrario il metodo remove elimina l’elemento che si trova in una determinata posizione, sposta di una posizione all’indietro tutti gli elementi che si trovano dopo l’elemento rimosso •Es: nomi.remove(0) •Il metodo size restituisce la dimensione del vettore •Es: nomi.size() Tipiche operazioni su di una collezione richiedono di esaminare tutti gli elementi, uno alla volta. Esempi: stampa, somma, ricerca di un elemento, minimo … Per un array o una lista si può usare un for for (int i = 0; i < arr.length; i++) System.out.println(arr[i]); for (int i = 0; i < list.size( ); i++) System.out.println(list.get(i)); Iterator Iterator astrae il processo di scandire gli elementi di un contenitore uno alla volta Permette di scandire gli elementi della struttura dati a prescindere dall’implementazione della struttura dati Metodi di Iterator Iterator supporta i seguenti metodi: • next() : restituisce il prossimo elemento nell’iteratore • hasNext() : testa se ci sono altri elementi nell’iterato L'interfaccia Java java.util.Iterator supporta anche il metodo remove che cancella l’elemento precedentemente restituito. Le classi che implementano Iterator possono non implementare remove public void remove() throws UnsupportedOperationException { throw new UnsupportedOperationException("remove"); } „Le strutture dati che supportano il metodo iterator() estendono la seguente interfaccia public interface Iterable { public Iterator iterator(); } Java fornisce l’interfaccia java.lang.Iterable Interfaccia Iterable Un iteratore è tipicamente associato ad una struttura dati che rappresenta una collezione Un iteratore di una sequenza deve restituire gli elementi nell’ordine lineare che hanno nella sequenza import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class ArralistIteratorExample { public static void main(String[] args) { //Create a list of names List names = new ArrayList(); //Add some names in list names.add("Eve"); names.add("Anna"); names.add("Tonny"); names.add("Steve"); System.out.println("Using Iterator"); Iterator iterator = names.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } } Esempio di Scansione con l'iterator For each System.out.println("Using Advanced for loop"); for (String name : names) { System.out.println(name); } Mappe Map Per chi non le avesse mai usate le HashMap sono una implementazione contenuta nelle API Java dell’interfaccia java.util.Map. La mappa (chiamata Dictionary nel mondo .Net) non è altro che una collezione di oggetti il cui scopo principale è quello di rendere veloci ed efficienti operazioni quali inserimento e ricerca di elementi. Per fare questo una mappa memorizza coppie (chiave, valore) e ha due implementazioni, del tutto generali: HashMap e TreeMap. Per approfondimenti potete consultare il nostro post sulle Java Collection. Tabelle L'interfaccia java.util.Map<K,V> specifica il tipo Map con chiavi di tipo K e valori associati di tipo VPer una descrizione esauriente si rimanda alla documentazione ufficiale, qui ci limitiamo a segnalare i metodi principali. • Il metodo V put(K key, V value) associa il valore value alla chiave key, sostituendo il valore precedente se già presente, viene reso il valore precedente se già presente, nullaltrimenti. • Il metodo V get(Object key) restituisce il valore associato alla chiave key, oppure null se la chiave non è presente. Può essere lanciata l'eccezione ClassCastException sekey non è di un tipo compatibile. • Il metodo V remove(Object key) rimuove la chiave key, e il valore associato se già presenti, viene reso il valore precedente se già presente, null altrimenti. Può essere lanciata l'eccezione ClassCastException se key non è di un tipo compatibile. • Il metodo boolean containsKey(Object key) restituisce true se la map contiene un'associazione per la chiave data. • Il metodo boolean containsKey(Object key) restituisce true se la map contiene un'associazione per la chiave data. • Il metodo boolean containsValue(Object value) restituisce true se la map contiene una o più chiavi associate al valore dato. • Il metodo Set<K> keySet() Restituisce un insieme (ovvero una Collection senza ripetizioni) che rappresenta le chiavi presenti nella tabella (ovviamente, senza ripetizioni). • Il metodo Collection<V> values() Restituisce una Collection che rappresenta i valori presenti nella tabella (ovviamente, ogni valore compare tante volte quante è presente in tabella). • Il metodo Set<Map.Entry<K,V>> entrySet() Restituisce una insieme di Map.Entry che rappresenta le coppie chiave - valore presenti nella tabella.Map.Entry è un "contenitore" che permette di stampare la coppia (attraverso il metodo toString implicito, o di accedere alla chiave o al valore con i metodi getKey() e getValue(). • Inoltre, tra gli altri, vi sono i metodi: clear(), isEmpty(), size(), equals(), dall'ovvio significato. Map<Object,Integer> h = new HashMap<Object,Integer>(); map.put(21, "Twenty One"); map.put(31, "Thirty One"); Iterator<Integer> keySetIterator = map.keySet().iterator(); while(keySetIterator.hasNext()){ Integer key = keySetIterator.next(); System.out.println("key: " + key + " value: " + map.get(key)); } Output: key: 21 value: Twenty One key: 31 value: Thirty One Esempio Hash Map Enumerativi public enum Giorno { LUNEDI, MARTEDI, MERCOLEDI, GIOVEDI, VENERDI, SABATO, DOMENICA // opzionalmente può terminare con ";" } FATE MOLTA ATTENZIONE ALLE MAIUSCOLE E ALLE MINUSCOLE Enumerativi Uso Le caratteristiche della classe enum Tecnicamente parlando in Java una enum è una classe come le altre ma che “implicitamente” (cioè senza che lo scriviamo noi) estende sempre la classe java.lang.Enum, cosa che ha l’unico inconveniente di rendere impossibile di scrivere enum che derivino da altri tipi. Il trattamento speciale che Java riserva agli enum riserva anche qualche interessante sorpresa: il compilatore per ogni classe enum sintetizza per noi un metodo statico (values) che ritorna un array di tutti i possibili valori che potranno assumere le variabili cha varanno come tipo l’enum, quindi nel nostro esempio il frammento di codice: Carratteristiche degli enum for( Giorno d : Giorno.values() ) { System.err.println(d); } Riassumiamo Enum risolve quindi un’esigenza reale, quella di definire un insieme di valori predefiniti, senza ricorrere alla mediazione di costanti intere, con la possibilità di avere una classe. Come abbiamo visto anche le enumerazioni possono essere utilizzate nei cicli for-each (for-in) e nei costrutti switch. Il metodo toString(), di default, è uguale al nome assegnato alla variabile, ma vedremo come poterlo modificare. Ecco una breve lista delle caratteristiche delle enumerazioni che ci aiuta a comprenderne la logica per utilizzarle al meglio: • Una enumerazione è una classe, in particolare l’estensione della classe java.lang.Enum, quindi come tale ha tutte le attenzioni sul controllo dei tipi in fase di compilazione. • I tipi definiti in una enumerazione sono istanze di classe, non tipi interi. • I valori di una enumerazione sono public final static, quindi immutabili. • Il metodo == è sovrascritto, quindi può essere usato in maniera intercambiabile al metodo equals. • Esiste la coppia di metodi valueOf()/toString() che possono essere sovrascritti. Oridamento Intefaccia comparable per ordinare Per ordinare liste di oggetti in Java sono a disposizione due interfacce: java.lang.Comparable java.util.Comparator L’interfaccia java.lang.comparable di solito si utilizza per definire l’ordinamento “naturale” di un oggetto e consta di un metodo con firma int compareTo(T o) che paragona l’oggetto corrente (this) con l’oggetto fornito in input al metodo (o). Il risultato sarà: un intero positivo se this>o 0 se sono uguali un intero negativo se this<0 public class Persona implements Comparable<Persona> { public Persona(){ } public Persona(String nome,String cognome,int eta){ this.nome=nome; this.cognome=cognome; this.eta=eta; } private String nome; private String cognome; private int eta; public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getCognome() { return cognome; } public void setCognome(String cognome) { this.cognome = cognome; } public int getEta() { return eta; } public void setEta(int eta) { this.eta = eta; } @Override public int compareTo(Persona o) { return this.cognome.compareTo(o.cognome); } } Esempio implementando compareTo L’ordinamento definito è sul cognome; si noti che utilizziamo il metodo compareTo dell’oggetto String che di default ordina in modo alfabetico crescente (A-Z). Sfrutto l’ordinamento basato sul cognome e che sfrutta il compareTo di String L’interfaccia java.util.Comparator si utilizza invece quando si vogliono definire ordinamenti alternativi dell’oggetto. Si crea dunque una classe a parte che implementa l’interfaccia con il seguente metodo: public int compare(T a,T b); Comparator Il risultato sarà: •un intero positivo se a>b •0 se sono uguali •un intero negativo se a<b import java.util.Comparator; public class PersonaEtaComparator implements Comparator<Persona> { @Override public int compare(Persona p1, Persona p2) { int retVal=0; if(p1.getEta()>p2.getEta()) { retVal=1; } else if(p1.getEta()<p2.getEta()) { retVal=-1; } return retVal; } } Implementazione public class TestOrdinamento { public static void main(String[] args) { LinkedList<Persona> lista=new LinkedList<Persona>(); lista.add(new Persona("mario","rossi",25)); lista.add(new Persona("luigi", "bianchi", 55)); lista.add(new Persona("mario","verdi",14)); lista.add(new Persona("luigi", "gialli", 45)); lista.add(new Persona("mario","maffei",32)); lista.add(new Persona("luigi", "servillo", 56)); System.out.println("Non ordinata>>"+lista); Collections.sort(lista); System.out.println("Ordinamento Naturale>>"+lista); Collections.sort(lista,new PersonaEtaComparator()); System.out.println("Ordinamento per età>>"+lista); } } Testiamone il Funzionamento