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