Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Roadmap
• 0. Primi passi con Java
• 7. Array e Collection
• 1. Buone abitudini
• 8. Progetto di classi
• 2. Tipi di dati primitivi
• 9. Ereditarietà
• 3. Uso di classi
• 10. Eccezioni
• 4. Leggere e scrivere
• 11. Stream
• 5. Definire metodi
• 12. Ricorsione
• 6. Strutture di controllo
• 13. Strutture dati +
Generics
Fondamenti di Programmazione
Strutture dati "classiche"
• Tipi di dati astratti, di uso generale, per i quali
sono definiti alcuni metodi standard
• Data la loro struttura generale e la loro utilità
universale, possono venire realizzate in
qualunque linguaggio di programmazione
• Spesso hanno natura ricorsiva
• Esamineremo dapprima le strutture fornite
dalla libreria Java, quindi vedemo come
scriverne noi
Fondamenti di Programmazione
Strutture dati + Generics
1
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Java Collection Framework
• A partire da Java 2, il package java.util
contiene il Java Collection Framework (JCF)
un insieme di interface, classi e classi astratte
progettate organicamente per facilitare la
creazione e l’utilizzo di vari tipi di strutture
dati collettive
• Alcune classi esistenti nelle versioni
precedenti (p.e. Vector) sono state riadattate
in modo da far parte del JCF
Fondamenti di Programmazione
Un framework complesso
• Il JCF ha subito costanti evoluzioni (e ingrandimenti) in
tutte le versioni di Java seguite alla 1.2
• Nella versione 6 esso comprende: 19 interface (5 di
supporto), 6 classi astratte, 12 classi istanziabili di uso
generale, 2 classi contenenti solo metodi static di
utilità, 16 classi per usi speciali, 2 classi di eccezioni
• Vedremo alcune nozioni generali, per un’analisi
dettagliata è più produttivo consultare la
documentazione
Fondamenti di Programmazione
Strutture dati + Generics
2
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Roadmap
• 13. Strutture dati + Generics
¾ Tipi di dati parametrici
Fondamenti di Programmazione
Tipi di dati parametrici
(da Java 1.5 in poi)
• Come già visto implicitamente per i Vector, le
definizioni di interface e classi nel JCF sono
parametriche rispetto ad altri tipi di dati
• Ad esempio, la nozione di Vector è quella di un
contenitore di qualunque tipo di dato al suo interno,
pertanto essa è parametrica rispetto al tipo di dato
contenuto
• Sintatticamente questo è espresso dalle parentesi
angolari <> che racchiudono il tipo di dato
parametrico
Fondamenti di Programmazione
Strutture dati + Generics
3
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Tipi di dati parametrici
public class Vector<E>
Vector<E>
La documentazione riporta la definizione che è parametrica rispetto ad
un tipo di dato generico E
. . .
Vector <String>
String> elenco = new Vector<
Vector<String>();
String>();
Quando si utilizza concretamente la classe, il tipo generico viene
sostituito con un tipo specifico (in questo caso String)
Fondamenti di Programmazione
Roadmap
• 13. Strutture dati + Generics
¾ Tipi di dati parametrici
¾ Le interface Collection, Iterable, Iterator
Fondamenti di Programmazione
Strutture dati + Generics
4
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface Collection
public interface Collection<E>
Collection<E> extends Iterable
• E’ l’interface basilare del JCF e rappresenta una
raccolta generica di oggetti di tipo E
• E’ implementata dalla maggior parte delle classi
(anche astratte) del JCF
• Specifica 15 metodi di cui 6 opzionali
• Uno dei metodi più significativi è iterator(), ereditato
a sua volta dall’interface Iterable
Fondamenti di Programmazione
L’interface Collection
Sorvoliamo su alcuni dettagli da
approfondire in seguito
boolean add(E
add(E e)
Ensures that this collection contains the specified element
(optional operation).
operation).
boolean addAll(
addAll(Collection<?
Collection<? extends E> c)
Adds all of the elements in the specified collection to this
collection (optional operation).
operation).
void clear()
clear()
Removes all of the elements from this collection (optional
operation).
operation).
boolean contains(
contains(Object o)
Returns true if this collection contains the specified element.
element.
boolean containsAll(
containsAll(Collection<?>
Collection<?> c)
Returns true if this collection contains all of the elements in
the specified collection.
collection.
boolean equals(
equals(Object o)
Compares the specified object with this collection for equality.
equality.
Fondamenti di Programmazione
Strutture dati + Generics
5
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface Collection
Sorvoliamo su alcuni dettagli da
approfondire in seguito
int hashCode()
hashCode()
Returns the hash code value for this collection.
collection.
boolean isEmpty()
isEmpty()
Returns true if this collection contains no elements.
elements.
Iterator<E>
Iterator<E> iterator()
iterator()
Returns an iterator over the elements in this collection.
collection.
boolean remove(
remove(Object o)
Removes a single instance of the specified element from this
collection,
collection, if it is present (optional operation).
operation).
boolean removeAll(
removeAll(Collection<?>
Collection<?> c)
Removes all of this collection's elements that are also
contained in the specified collection (optional operation).
operation).
boolean retainAll(
retainAll(Collection<?>
Collection<?> c)
Retains only the elements in this collection that are contained
in the specified collection (optional operation).
operation).
Fondamenti di Programmazione
L’interface Collection
Sorvoliamo su alcuni dettagli da
approfondire in seguito
int size()
size()
Returns the number of elements in this collection.
collection.
Object[]
Object[] toArray()
toArray()
Returns an array containing all of the elements in this
collection.
collection.
<T> T[] toArray(T[]
toArray(T[] a)
Returns an array containing all of the elements in this
collection;
collection; the runtime type of the returned array is that of
the specified array.
array.
Fondamenti di Programmazione
Strutture dati + Generics
6
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface Iterable
public interface Iterable<T>
• L’interface Iterable specifica un solo metodo:
Iterator<T> iterator()
• Il metodo iterator() restituisce un riferimento
a un Iterator<T> ovvero a un iteratore sul
tipo di oggetti contenuti nella Collection
• Iterator è a sua volta un’interface
Fondamenti di Programmazione
L’interface Iterator
public interface Iterator<E>
Iterator<E>
• L’interface Iterator specifica i metodi che deve
fornire un “iteratore” ovvero un oggetto capace di
scorrere uno a uno gli elementi di una raccolta
boolean hasNext()
hasNext()
Returns true if the iteration has more elements.
elements.
E next()
next()
Returns the next element in the iteration.
iteration.
void remove()
remove()
Removes from the underlying collection the last
element returned by the iterator (optional
operation).
operation).
Fondamenti di Programmazione
Strutture dati + Generics
7
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Uso di iteratori
• Gli iteratori offrono un modo alternativo (e
tecnicamente più efficiente) di eseguire un
operazione su tutti gli elementi di una
Collection
Vector <String>
String> elenco = new Vector<
Vector<String>();
String>();
. . .
Iterator<
();
Iterator<String>
String> lancetta = elenco.iterator
elenco.iterator();
while (lancetta.hasNext
())
(lancetta.hasNext())
System.out.println
(lancetta.next
next());
());
System.out.println(lancetta.
Fondamenti di Programmazione
Roadmap
• 13. Strutture dati + Generics
¾ Tipi di dati parametrici
¾ Le interface Collection, Iterable, Iterator
¾ Famiglie di Collection: Set, List, Queue, Deque
Fondamenti di Programmazione
Strutture dati + Generics
8
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Famiglie di Collection
• La documentazione di JCF indica 3 principali
famiglie di Collection, corrispondenti alle
seguenti interface che ereditano da
Collection:
– Set<E>
– List <E>
– Queue<E> e Deque <E>
Fondamenti di Programmazione
L’interface Set
• Un Set è una Collection con il vincolo
aggiuntivo di non contenere elementi duplicati
(come gli insiemi della teoria degli insiemi)
• Di fatto non aggiunge la specifica di nuovi
metodi a quelli ereditati da Collection ma
aumenta i requisiti di alcuni metodi (p.e. add)
in modo che sia garantita la non duplicazione
Fondamenti di Programmazione
Strutture dati + Generics
9
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Le interface SortedSet
• L’interface SortedSet estende Set con il requisito che
esista una relazione di ordine totale tra gli elementi
(siano tutti confrontabili)
• Questo rende possibile la specifica di metodi
aggiuntivi:
– la selezione del primo o ultimo elemento nell’ordine
– la selezione di tutti gli elementi prima o dopo un certo
elemento
– la selezione degli elementi in un certo range
Fondamenti di Programmazione
Le interface NavigableSet
• L’interface NavigableSet estende SortedSet
con svariati metodi di utilità aggiuntiva tra i
quali:
– la selezione dell’elemento più vicino (prima o dopo
nell’ordine, con o senza uguaglianza) rispetto a un
elemento specificato
– la possibilità di enumerare il Set in ordine inverso
– ...
Fondamenti di Programmazione
Strutture dati + Generics
10
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface List
• Una List è una Collection nella quale gli
elementi sono ordinati posizionalmente e
sono disponibili operazioni (accesso,
inserimento, eliminazione, ricerca, estrazione,
sostituzione) riferite ad indici posizionali
• Oltre all’Iterator “modello base” è possibile
creare un ListIterator che permette
“movimenti” bidirezionali sulla List ed
operazioni di modifica del contenuo
Fondamenti di Programmazione
Operazioni posizionali su List
Inserimento
void add(
add(int index,
index, E element)
element)
Inserts the specified element at the specified position in this
list (optional operation).
operation).
boolean addAll(
addAll(int index,
index, Collection<?
Collection<? extends E> c)
Inserts all of the elements in the specified collection into this
list at the specified position (optional operation).
operation).
E get(
get(int index)
index)
Returns the element at the specified position in this list.
Accesso
int indexOf(
indexOf(Object o)
Returns the index of the first occurrence of the specified element
in this list, or -1 if this list does not contain the element.
element.
int lastIndexOf(
lastIndexOf(Object o)
Returns the index of the last occurrence of the specified element
in this list, or -1 if this list does not contain the element.
element.
Ricerca: si cerca un elemento tale che o.equals(elemento) sia true
Fondamenti di Programmazione
Strutture dati + Generics
11
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Operazioni posizionali su List
Eliminazione
E remove(
remove(int index)
index)
Removes the element at the specified position in this list
(optional operation).
operation).
E set(int
set(int index,
index, E element)
element)
Replaces the element at the specified position in this list with
the specified element (optional operation).
operation).
Sostituzione
List<E> subList(
subList(int fromIndex,
fromIndex, int toIndex)
toIndex)
Returns a view of the portion of this list between the specified
fromIndex,
fromIndex, inclusive, and toIndex,
toIndex, exclusive.
exclusive.
Estrazione di una sottolista tra due posizioni
(la prima inclusa, la seconda no)
Fondamenti di Programmazione
L’interface ListIterator
• Si può creare un ListIterator in due modi (a
partire dall’inizio della lista o da una posizione
specificata)
ListIterator<E>
ListIterator<E> listIterator()
listIterator()
Returns a list iterator over the elements in this
list (in proper sequence).
sequence).
ListIterator<E>
ListIterator<E> listIterator(
listIterator(int index)
index)
Returns a list iterator of the elements in this
list (in proper sequence),
sequence), starting at the
specified position in this list.
Fondamenti di Programmazione
Strutture dati + Generics
12
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface ListIterator
• Oltre ai due metodi per “scorrere avanti” ed al
remove disponibili in Iterator, offre:
– due metodi duali per “scorrere indietro”
– due metodi per sapere in che posizione è
l’iteratore
– aggiunta e sostituzione di un elemento
Fondamenti di Programmazione
L’interface ListIterator
Scorrimento “all’indietro”
boolean hasPrevious()
hasPrevious()
Returns true if this list iterator has more elements
when traversing the list in the reverse direction.
E previous()
previous()
Returns the previous element in the list.
int nextIndex()
nextIndex()
Returns the index of the element that would be returned
by a subsequent call to next.
next.
int previousIndex()
previousIndex()
Returns the index of the element that would be returned
by a subsequent call to previous.
previous.
Posizione dell’iteratore
Fondamenti di Programmazione
Strutture dati + Generics
13
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface ListIterator
void add(E
add(E e)
Inserts the specified element into the list
(optional operation).
operation).
L’operazione di aggiunta è riferita alla posizione dell’iteratore
void set(E e)
Replaces the last element returned by next or
previous with the specified element (optional
operation).
operation).
void remove()
remove()
Removes from the list the last element that was
returned by next or previous (optional operation).
operation).
Le operazioni set e remove sono riferite all’ultimo elemento acceduto con
next o previous (se non esiste o è stato già eliminato, si ha un’eccezione)
Fondamenti di Programmazione
Prima di add
...
0
1
2
previous
next
size()-1
Fondamenti di Programmazione
Strutture dati + Generics
14
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Dopo add
...
0
1
2
3
previous
next
Fondamenti di Programmazione
L’interface Queue
• Rappresenta una coda ovvero una struttura
dove si inseriscono elementi (ingresso in
coda) e solo un elemento (la testa della coda)
è disponibile per l’accesso o per l’estrazione
(uscita dalla coda)
• La politica di selezione dell’elemento in testa
è tipicamente FIFO ma sono possibili anche
code con “scavalcamento” basato su qualche
criterio di ordinamento o priorità
Fondamenti di Programmazione
Strutture dati + Generics
15
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface Queue
• Esistono tre operazioni base su una Queue:
– inserimento di un elemento in coda
– accesso all’elemento in testa (senza toglierlo dalla
coda)
– estrazione dell’elemento in testa (eliminandolo
dalla coda)
• Di ognuna esistono due versioni a seconda
del comportamento quando l’operazione non
è possibile: una lancia un’eccezione, l’altra
ritorna false o null
Fondamenti di Programmazione
L’interface Queue
• Lanciano eccezione
• Non lanciano eccezione
boolean add(E e)
Aggiunta elemento
boolean offer(E e)
Aggiunta elemento
E element()
Accesso elemento in testa
E peek()
Accesso elemento in testa
E remove()
Estrazione elemento in testa
E poll()
Estrazione elemento in testa
Fondamenti di Programmazione
Strutture dati + Generics
16
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface Deque
• Il nome Deque sta per “double ended queue” è
un’interface che estende Queue poiché rappresenta
una struttura con due estremi (testa e coda, head e
tail) ed operazioni di inserimento, accesso ed
estrazione su entrambi gli estremi
• Come nel caso precedente, per ogni operazione
esistono due versioni (una lancia eccezioni, una no)
• I metodi ereditati da Queue sono “affiancati” da
metodi equivalenti con nomi più specifici
Fondamenti di Programmazione
Deque: operazioni in testa
• Lanciano eccezione
• Non lanciano eccezione
boolean addFirst(E e)
Aggiunta elemento in testa
boolean offerFirst (E e)
Aggiunta elemento in testa
E getFirst ()
Accesso elemento in testa
E peekFirst ()
Accesso elemento in testa
E removeFirst ()
Estrazione elemento in testa
E pollFirst ()
Estrazione elemento in testa
Fondamenti di Programmazione
Strutture dati + Generics
17
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Deque: operazioni in coda
• Lanciano eccezione
• Non lanciano eccezione
boolean addLast(E e)
Aggiunta elemento in coda
boolean offerLast (E e)
Aggiunta elemento in coda
E getLast ()
Accesso elemento in coda
E peekLast ()
Accesso elemento in coda
E removeLast ()
Estrazione elemento in coda
E pollLast ()
Estrazione elemento in coda
Fondamenti di Programmazione
Deque: metodi aggiuntivi
Iterator<E>
Iterator<E> descendingIterator()
descendingIterator()
Returns an iterator over the elements in this deque in reverse
sequential order.
order.
boolean removeFirstOccurrence(
removeFirstOccurrence(Object o)
Removes the first occurrence of the specified element from this
deque.
deque.
boolean removeLastOccurrence(
removeLastOccurrence(Object o)
Removes the last occurrence of the specified element from this
deque.
deque.
Fondamenti di Programmazione
Strutture dati + Generics
18
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Roadmap
• 13. Strutture dati + Generics
¾ Tipi di dati parametrici
¾ Le interface Collection, Iterable, Iterator
¾ Famiglie di Collection: Set, List, Queue, Deque
¾ La nozione di Stack
Fondamenti di Programmazione
La nozione di stack (pila)
Operazioni solo
sulla cima della pila:
push (aggiunta),
pop (estrazione),
peek (accesso)
Fondamenti di Programmazione
Strutture dati + Generics
19
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
La nozione di stack
• Uno stack è una forma limitata di Deque nella
quale si opera sempre in testa e mai in coda
• L’interface Deque specifica tre metodi con i
nomi “tipici” della nozione di stack (essi
“affiancano” tre metodi equivalenti della
specifica generale)
Fondamenti di Programmazione
Deque: metodi per uso a
stack
• Nomi metodi stack
• Metodi “equivalenti”
void push(E e)
Aggiunta elemento in cima
boolean addFirst(E e)
Aggiunta elemento in testa
E peek ()
Accesso elemento in cima
E peekFirst ()
Accesso elemento in testa
E pop ()
Estrazione elemento in cima
E removeFirst ()
Estrazione elemento in testa
Fondamenti di Programmazione
Strutture dati + Generics
20
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Ripensamenti
• Il package java.util contiene (da sempre)
anche una classe Stack, derivata da Vector,
con i tre metodi push, pop e peek
• Il suo uso è tuttavia “sconsigliato” e si
suggerisce di usare un’implementazione di
Deque nel modo sopra indicato
• La classe Stack non fa parte del JCF
Fondamenti di Programmazione
Roadmap
• 13. Strutture dati + Generics
¾ Tipi di dati parametrici
¾ Le interface Collection, Iterable, Iterator
¾ Famiglie di Collection: Set, List, Queue, Deque
¾ La nozione di Stack
¾ L’interface Map
Fondamenti di Programmazione
Strutture dati + Generics
21
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface Map
• Il JCF non si basa solo sull’interface Collection ma
anche sull’interface “parallela” Map<K,V>
• Mentre la Collection è una raccolta “semplice” di
elementi, la Map è una raccolta indicizzata di
elementi: ogni elemento viene inserito
accompagnato da un altro elemento detto chiave
(key) che lo identifica univocamente
• Questo permette di accedere agli elementi di una
Map non solo tramite iterazione o accesso
posizionale, ma anche tramite accesso diretto basato
su chiave
Fondamenti di Programmazione
Uso di chiavi
• L’uso di chiavi univoche è molto diffuso per
l’identificazione di elementi:
– codice fiscale per le persone
– matricola per studenti universitari
– targhe per autoveicoli
– codice a tre lettere per aeroporti (e alfanumerico
per ciascun volo)
– codici di prenotazione
Fondamenti di Programmazione
Strutture dati + Generics
22
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface Map
• L’interfaccia Map<K, V> è parametrica
rispetto a due tipi di dati:
– K rappresenta il tipo usato come chiave
– V rappresenta il tipo degli elementi contenuti nella
Map e indicizzati dalle chiavi
Fondamenti di Programmazione
L’interface Map: relazioni
con Collection
• Anche se non è formalmente derivata
dall’interface Collection, l’interface Map ha
alcune “parentele” con essa:
– alcuni metodi, (clear, isEmpty, size) con lo stesso
nome e lo stesso ruolo che hanno in Collection
– tre possibilità di accedere al contenuto di una Map
sotto forma di Collection
Fondamenti di Programmazione
Strutture dati + Generics
23
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface Map: aggiunta
con metodo put
V put(K
put(K key,
key, V value)
value)
Se la key passata come argomento non è presente nella map,
viene aggiunta la nuova coppia key-value
Se invece la key è già presente, il nuovo value sostituisce quello
precedentemente associato alla key
Fondamenti di Programmazione
L’interface Map: metodo put
put("VRN",
put("VRN", new Airport("Verona",
Airport("Verona", . . .))
“FCO”
Roma Fiumicino
…
…
“MXP”
Milano Malpensa
…
…
“BGY”
Bergamo
…
…
Fondamenti di Programmazione
Strutture dati + Generics
24
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface Map: metodo put
put("VRN",
put("VRN", new Airport("Verona",
Airport("Verona", . . .))
“VRN”
“FCO”
Verona
…
…
Roma Fiumicino
…
…
“MXP”
Milano Malpensa
…
…
“BGY”
Bergamo
…
…
Fondamenti di Programmazione
L’interface Map: metodo put
put("BGY",
put("BGY", new Airport("Milano
Airport("Milano Orio", . . .))
“VRN”
“FCO”
Verona
…
…
Roma Fiumicino
…
…
“MXP”
Milano Malpensa
…
…
“BGY”
Bergamo
…
…
Fondamenti di Programmazione
Strutture dati + Generics
25
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface Map: metodo put
put("BGY",
put("BGY", new Airport("Milano
Airport("Milano Orio", . . .))
“VRN”
“FCO”
Verona
…
…
Roma Fiumicino
…
…
“MXP”
Milano Malpensa
…
…
“BGY”
Milano Orio
…
…
Fondamenti di Programmazione
L’interface Map: accesso
(con o senza eliminazione)
V remove (Object key)
key)
Se la key passata come argomento è presente nella map, viene
restituito un riferimento al value corrispondente e la coppia keyvalue viene eliminata dalla map, altrimenti viene restituito null
V get(
get(Object key)
key)
Se la key passata come argomento è presente nella map, viene
restituito un riferimento al value corrispondente, altrimenti
viene restituito null. Il contenuto della Map rimane immutato.
Fondamenti di Programmazione
Strutture dati + Generics
26
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface Map:
verifiche di presenza
boolean containsKey(Object key)
Returns true if this map contains a mapping
for the specified key.
boolean containsValue(Object value)
Returns true if this map maps one or more keys
to the specified value.
Fondamenti di Programmazione
L’interface Map:
vista come Collection
• Sono specificati metodi per:
– ottenere una Collection contenente tutti i values
presenti nella Map
Collection<V>
Collection<V> values()
values()
Returns a Collection view of the values contained in
this map.
map.
– ottenere un Set contenente tutte le keys presenti
nella Map
Set<K> keySet()
keySet()
Returns a Set view of the keys contained in this map.
map.
Fondamenti di Programmazione
Strutture dati + Generics
Per definizione non ci possono
essere chiavi duplicate
27
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
L’interface Map:
vista come Collection
– ottenere un Set contenente tutte le coppie keyvalue presenti nella Map
Set<Map
.Entry<K,V>> entrySet()
Set<Map.Entry<K,V>>
entrySet()
Returns a Set view of the mappings contained in this
map.
map.
Poiché non ci possono essere chiavi
duplicate nemmeno le coppie possono
essere duplicate
Gli elementi del Set implementano
un’apposita interface dedicata alla
rappresentazione di coppie key-value
Fondamenti di Programmazione
Le interface SortedMap e
NavigableMap
• In modo analogo a quanto visto per Set,
l’interface SortedMap estende Map
aggiungendo un requisito di ordinamento
totale sulle chiavi (e relativi metodi di utilità)
• L’interface NavigableMap estende SortedMap
con altri metodi di utilità (concettualmente
analoghi a quelli di NavigableSet)
Fondamenti di Programmazione
Strutture dati + Generics
28
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Roadmap
• 13. Strutture dati + Generics
¾ Tipi di dati parametrici
¾ Le interface Collection, Iterable, Iterator
¾ Famiglie di Collection: Set, List, Queue, Deque
¾ La nozione di Stack
¾ L’interface Map
¾ Dalle interface alle classi
Fondamenti di Programmazione
Le classi ArrayList e Vector
• La classi ArrayList e Vector forniscono entrambe
un’implementazione dell’interface List
• La classe ArrayList fornisce in aggiunta 3 metodi di
natura accessoria
• La classe Vector ha svariati metodi ulteriori che
riflettono il suo progetto precedente (metodi
“affiancati” con nomi diversi ma funzionalità
identiche, operazioni sulla struttura interna di uso
piuttosto raro, …)
Fondamenti di Programmazione
Strutture dati + Generics
29
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Le classi ArrayList e Vector
• La classe ArrayList è leggermente più efficiente di
Vector, in compenso ArrayList non è predisposta per
la programmazione multi-thread (argomento trattato
in corsi successivi) mentre Vector sì
• Negli usi più comuni ArrayList e Vector possono
essere considerate all’incirca equivalenti
• Alcuni libri di testo suggeriscono di usare ArrayList
anziché Vector in quanto ArrayList è stata progettata
nel contesto di JCF, ma non ci sono forti motivi per
preferire l’una o l’altra a livello didattico iniziale
Fondamenti di Programmazione
La classe ArrayDeque
• La classe ArrayDeque offre
un’implementazione dell’interface Deque
senza alcun metodo aggiuntivo
• Non è predisposta per la programmazione
multi-thread
Fondamenti di Programmazione
Strutture dati + Generics
30
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
La classe LinkedList
• La classe LinkedList ha una natura
“multiforme” in quanto implementa sia
l’interface List sia l’interface Deque (può
quindi essere usata in alternativa ad
entrambe)
• Non è predisposta per la programmazione
multi-thread
Fondamenti di Programmazione
Le classi HashSet e
LinkedHashSet
• La classe HashSet fornisce una semplice
implementazione dell’interface Set (il nome
della classe deriva dalla struttura dati interna
utilizzata per l’implementazione e non visibile)
• La classe LinkedHashSet è figlia di HashSet e
aggiunge la caratteristica di mantenere
sempre lo stesso ordine quando si effettua
l’enumerazione degli elementi (non garantito
da HashSet)
Fondamenti di Programmazione
Strutture dati + Generics
31
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
La classe TreeSet
• La classe TreeSet fornisce
un’implementazione dell’interface
NavigableSet (il nome della classe deriva dalla
struttura dati interna utilizzata per
l’implementazione e non visibile)
• Le tre classi HashSet, LinkedHashSet e
TreeSet non sono predisposte per la
programmazione multi-thread
Fondamenti di Programmazione
Le classi HashMap e
Hashtable
• Entrambe le classi HashMap e Hashtable forniscono
un’implementazione dell’interface Map
• La classe Hashtable ha alcuni metodi aggiuntivi che
riflettono il suo progetto preesistente, inoltre non
ammette elementi o chiavi null (mentre HashMap sì)
ed è predisposta per la programmazione multi-thread
(mentre HashMap no)
• Il rapporto tra HashMap e Hashtable è simile a quello
tra ArrayList e Vector
Fondamenti di Programmazione
Strutture dati + Generics
32
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
La classe LinkedHashMap
• La classe LinkedHashMap è figlia di HashMap
e aggiunge la caratteristica di mantenere
sempre lo stesso ordine quando si effettua
l’enumerazione degli elementi (non garantito
da HashMap)
• Il rapporto tra LinkedHashMap ed HashMap è
del tutto analogo a quello tra LinkedHashSet
ed HashSet
Fondamenti di Programmazione
La classe TreeMap
• La classe TreeMap fornisce
un’implementazione dell’interface
NavigableMap (il nome della classe deriva
dalla struttura dati interna utilizzata per
l’implementazione e non visibile)
• Le tre classi HashMap, LinkedHashMap e
TreeMap non sono predisposte per la
programmazione multi-thread
Fondamenti di Programmazione
Strutture dati + Generics
33
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Classi “solo static”: Arrays
• La classe Arrays contiene 105 metodi (molti in
overloading)
overloading) per svolgere comuni operazioni utili su array
(di tipi semplici o di Object)
Object) tra le quali:
– asList:
asList: restituzione di una List con il contenuto dell’array
– binarySearch (18 versioni): ricerca efficiente di un elemento
(richiede che l’array sia ordinato)
– copyOf (10 versioni): crea una copia dell’array in un nuovo array
– copyOfRange (10 versioni): crea una copia di un range dell’array
in un nuovo array
– equals (9 versioni): confronta il contenuto di due array
– fill (18 versioni): riempimento con un certo valore
– sort (18 versioni): ordinamento
– toString (9 versioni)
Fondamenti di Programmazione
Classi “solo static”:
Collections
• La classe Collections contiene 52 metodi per svolgere
svariate operazioni (anche non molto comuni) su
Collection tra le quali:
– binarySearch (2 versioni)
– copy
– fill
– max e min (2 versioni ciascuno)
– replaceAll
– reverse
– rotate
– shuffle (2 versioni)
– sort (2 versioni)
– swap
Fondamenti di Programmazione
Strutture dati + Generics
34
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Roadmap
• 13. Strutture dati + Generics
¾ Tipi di dati parametrici
¾ Le interface Collection, Iterable, Iterator
¾ Famiglie di Collection: Set, List, Queue, Deque
¾ La nozione di Stack
¾ L’interface Map
¾ Dalle interface alle classi
¾ Guardando dentro: classi e interface generiche
Fondamenti di Programmazione
Classi e interface generiche
• Le interface e le classi appartenenti al JCF sono
definite “genericamente” rispetto al tipo di oggetto
contenuto in ciascuna specifica istanza di Collection
• Tale tipo generico è specificato tra <> e viene detto
parametro di tipo o variabile di tipo
• La definizione di classi generiche (ossia con uno o
più parametri di tipo) è una delle principali
innovazioni introdotte nella versione 1.5
• Essa è particolarmente adatta alle strutture dati ma
può essere utilizzata ovunque lo si ritenga utile
Fondamenti di Programmazione
Strutture dati + Generics
35
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Confronto di stringhe
descrittive
• Si supponga di voler confrontare le stringhe
descrittive di oggetti dello stesso tipo in
questo modo:
– se hanno una parte iniziale uguale (non vuota)
questa viene restituita in uscita
– in caso contrario viene restituito null
Fondamenti di Programmazione
Prima della 1.5 …
• Poiché il metodo toString è definito nella classe
Object e il tipo Object è compatibile con
qualunque altro tipo, si potrebbe definire un
metodo di confronto che riceve in ingresso due
Object
String confrontaDescrizioni(
confrontaDescrizioni(Object obj1,Object
obj1,Object obj2)
• . . . così però potrei finire per confrontare le
pere con le mele (oggetti di tipo diverso)
Fondamenti di Programmazione
Strutture dati + Generics
36
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
La classe Object come jolly
• Prima della versione 1.5 la classe Object veniva
spesso usata come “jolly” per indicare “qualunque
classe”
• Ad esempio tutte le strutture dati del JCF erano
definiti come contenitori di Object, cosa che
permetteva di “mescolare” oggetti diversi nella
stessa struttura . . .
• . . . ma obbligava a fare il cast esplicito per accedere
agli elementi contenuti secondo il loro “vero” tipo
Fondamenti di Programmazione
Due requisiti in conflitto ?
• Nella maggior parte dei casi non si desiderano
strutture con contenuto eterogeneo, anzi la presenza
di elementi eterogenei è indice di un errore e
potrebbe causare eccezioni al momento del cast
• Tuttavia si desidera la definizione di strutture dati e
metodi generici, cioè in grado di operare su
qualunque classe (altrimenti dovremmo definire una
nuova classe contenitore per ogni nuova classe di
oggetto contenuto)
Fondamenti di Programmazione
Strutture dati + Generics
37
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Due requisiti in conflitto ?
• Genericità: deve essere possibile definire
classi generiche con capacità “universali” o
comunque riferite ad un ampio insieme di
classi
• Type safety: nell’ambito di classi generiche si
vogliono evitare mescolanze indesiderate di
tipi. Eventuali errori sui tipi devono essere
rilevati al momento della compilazione
anziché generare eccezioni in esecuzione.
Fondamenti di Programmazione
Due requisiti in conflitto ?
• Nelle versioni precedenti alla 1.5 i due
requisiti erano effettivamente in conflitto: per
ottenere la genericità si usava Object come
jolly, rinunciando così alla type safety
• A partire dalla versione 1.5 si possono
soddisfare entrambi i requisiti usando il
meccanismo delle classi generiche
Fondamenti di Programmazione
Strutture dati + Generics
38
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Una classe generica
La definizione della classe è
parametrica rispetto ad un tipo T
public class ComparatoreDescrizioni <T>
{
private T obj1; La classe ha due
private T obj2; attributi del tipo T
public
{
obj1
obj2
}
ComparatoreDescrizioni(T
ComparatoreDescrizioni(T _obj1, T _obj2)
= _obj1;
= _obj2;
Il costruttore riceve due
argomenti formali di tipo T
public void inizioComune()
inizioComune()
{// DETTAGLI RIPORTATI IN SEGUITO MA NON RILEVANTI
}
}
Fondamenti di Programmazione
Una classe generica
public String inizioComune()
inizioComune()
{
String descr1 = obj1.toString
();
obj1.toString();
String descr2 = obj2.toString
();
obj2.toString();
int i = 0;
StringBuffer parteUguale = new StringBuffer();
StringBuffer();
while (i < descr1.length
() && i < descr2.length
() &&
descr1.length()
descr2.length()
descr1.charAt
(i) == descr2.charAt
(i))
descr1.charAt(i)
descr2.charAt(i))
{
parteUguale.
charAt(i));
(i));
parteUguale.append(descr1.
append(descr1.charAt
i++;
}
String inizio = parteUguale.
parteUguale.toString();
toString();
if (inizio.length
() == 0)
(inizio.length()
return null;
null;
else
return inizio;
}
Fondamenti di Programmazione
Strutture dati + Generics
39
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Un main che la utilizza
public class MainComparatoreString
{
public static void main (String[]
String[] args)
args)
{
String s1 = MyUtil.
MyUtil.leggiString("Inserire
leggiString("Inserire il primo termine");
String s2 = MyUtil.
MyUtil.leggiString("Inserire
leggiString("Inserire il secondo termine");
ComparatoreDescrizioni<
ComparatoreDescrizioni<String>
String> prova =
new ComparatoreDescrizioni<
ComparatoreDescrizioni<String>(s1,s2);
String>(s1,s2);
String risultato= prova.inizioComune
();
prova.inizioComune();
System.out.println
(risultato);
System.out.println(risultato);
}
Il tipo generico T viene sostituito
da String in questo utilizzo
}
Se s1 ed s2 non fossero
di tipo String si avrebbe
errore in compilazione
Fondamenti di Programmazione
Un altro main che la utilizza
public class MainComparatoreDatiPersonali
{
. . .
public static void main (String [] args)
args)
{
DatiPersonali dati1 = creaDati();
creaDati();
DatiPersonali dati2 = creaDati();
creaDati();
ComparatoreDescrizioni <DatiPersonali>
DatiPersonali> prova =
new ComparatoreDescrizioni<
ComparatoreDescrizioni<DatiPersonali>
DatiPersonali> (dati1,dati2);
String risultato = prova.inizioComune
();
prova.inizioComune();
. . .
}
. . .
Il tipo generico T viene sostituito
da DatiPersonali in questo utilizzo
Fondamenti di Programmazione
Strutture dati + Generics
40
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Classi generiche:
regole e convenzioni
• La definizione di una classe generica è
parametrica rispetto ad uno o più parametri di
tipo, indicati tra <> dopo il nome della classe
• Per convenzione i parametri di tipo vengono
indicati con singole lettere maiuscole:
T, S, U per qualunque uso
E per indicare elementi di strutture collettive
K,V per indicare chiave e valore di Map
Fondamenti di Programmazione
Classi generiche:
regole e convenzioni
• I nomi dei parametri di tipo possono essere
usati (quasi) come qualunque altro nome di
tipo nella definizione di una classe ma ci sono
delle limitazioni:
– non è possibile creare istanze di tipi parametrici
(un’istruzione new T non è possibile)
– non è possibile definire attributi static di un tipo
parametrico o usare un tipo parametrico
all’interno di metodi static
Fondamenti di Programmazione
Strutture dati + Generics
41
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Classi generiche:
regole e convenzioni
• Al momento dell’uso ciascun parametro di
tipo generico deve essere sostituito con un
tipo strutturato
• I tipi elementari non sono utilizzabili a questo
scopo
Fondamenti di Programmazione
Metodi generici
• In qualunque classe (generica o no) è
possibile definire metodi generici, la cui
definizione dipende da uno o più variabili di
tipo (riferite al solo metodo, non alla classe di
cui fa parte)
• Le variabili di tipo sono specificate tra <>
prima del tipo ritornato
• Esse devono comparire anche nella
definizione del tipo di argomenti formali
Fondamenti di Programmazione
Strutture dati + Generics
42
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Metodi generici
• Se un metodo generico è definito in una
classe generica non bisogna far confusione
tra variabili di tipo del solo metodo e variabili
di tipo dell’intera classe
• Spesso i metodi generici sono static e definiti
in classi non generiche
• Esempi di metodi generici static sono presenti
nelle classi (non generiche) Arrays e
Collections
Fondamenti di Programmazione
Un esempio di metodo
generico (classe Arrays)
Il metodo copyOf è generico
rispetto al tipo T
Il metodo restituisce
un array di T
Il primo argomento
formale del metodo è a
sua volta un array di T
static <T> T[] copyOf(T[]
copyOf(T[] original,
original, int newLength)
newLength)
Copies the specified array,
array, truncating or padding
with nulls (if necessary)
necessary) so the copy has the
specified length.
length.
Fondamenti di Programmazione
Strutture dati + Generics
43
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Metodi generici
• L’invocazione di un metodo generico non
richiede di specificare esplicitamente la
sostituzione tra parametri di tipo e tipi
effettivamente utilizzati: il compilatore
riconosce la sostituzione dal passaggio dei
parametri (purchè compatibile con la
definizione)
• Anche per i metodi generici non è possibile
utilizzare tipi elementari come sostituti di
parametri di tipo
Fondamenti di Programmazione
Vincoli sui parametri di tipo
• In alcuni casi non si vuole che una classe
generica sia “universale” (cioè che ciascun
parametro di tipo sia sostituibile da
qualunque tipo strutturato): il tipo generico
può essere vincolato a rispettare delle
caratteristiche
• I vincoli specificabili sono legati alle relazioni
di ereditarietà
Fondamenti di Programmazione
Strutture dati + Generics
44
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Vincolo extends
• Si può imporre al tipo generico di essere
derivato da una certa classe o interface
• In questo modo qualunque tipo sostituito
avrà delle caratteristiche comuni garantite
sulle quali la definizione della classe generica
può fare affidamento
• Il vincolo viene espresso tra le <> usando la
parola chiave extends p.e.
<T extends Comparable>
Fondamenti di Programmazione
Vincolo super
• In modo analogo è possibile imporre il vincolo
di essere superclasse di un tipo specificato
• Il vincolo è espresso usando la parola chiave
super tra <>
Fondamenti di Programmazione
Strutture dati + Generics
45
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Il carattere “jolly” (wildcard)
• Il carattere ? all’interno di <> rappresenta
l’indicazione jolly di “qualunque tipo”
• Esso è utilizzabile in combinazione con i
vincoli extends o super o da solo
• Se usato da solo esso indica una sostituzione
totalmente libera (che però potrebbe essere
vincolata indirettamente da altre parti della
definizione della classe o del metodo)
Fondamenti di Programmazione
Esempi di vincoli e jolly
Il metodo copy (classe
Collections) è generico
rispetto al tipo T
Esso riceve come argomenti una List
“sorgente” src da cui copiare gli elementi e
una List “destinazione” dest dove metterli.
Poiché List è un’interface generica va
specificato il tipo contenuto di entrambe
static <T> void copy(List<? super T> dest, List<? extends T> src)
Copies all of the elements from one list into another.
La List destinazione
deve contenere
elementi di una
superclasse di T
La List sorgente deve
contenere elementi di
una sottoclasse di T
Fondamenti di Programmazione
Strutture dati + Generics
46
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Esempi di vincoli e jolly
Il metodo addAll è definito
nell’interface Collection che è
parametrica rispetto al tipo E dei
suoi elementi (qui richiamato)
Esso riceve come argomento
una Collection c da cui
“pescare” gli elementi da
aggiungere
boolean addAll(
addAll(Collection<?
Collection<? extends E> c)
Adds all of the elements in the specified collection to
this collection (optional operation).
operation).
La Collection c deve
contenere elementi di
una sottoclasse di E
Fondamenti di Programmazione
Esempi di vincoli e jolly
Il metodo containsAll è definito
nell’interface Collection che è
parametrica rispetto al tipo E dei
suoi elementi (qui non richiamato)
Esso riceve come argomento una
Collection c da cui “pescare” gli
elementi di cui verificare la
presenza
boolean containsAll(
containsAll(Collection<?>
Collection<?> c)
Returns true if this collection contains all of the
elements in the specified collection.
collection.
La Collection c può
contenere elementi di
qualunque classe
Fondamenti di Programmazione
Strutture dati + Generics
47
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Roadmap
• 13. Strutture dati + Generics
¾ Tipi di dati parametrici
¾ Le interface Collection,
Collection, Iterable,
Iterable, Iterator
¾ Famiglie di Collection:
Collection: Set, List, Queue,
Queue, Deque
¾ La nozione di Stack
¾ L’interface Map
¾ Dalle interface alle classi
¾ Guardando dentro: classi e interface generiche
¾ Esempi di programmazione di strutture dati “classiche”:
liste concatenate e alberi binari
Fondamenti di Programmazione
Liste concatenate
• Si parla di lista concatenata per indicare una
struttura nella quale gli elementi (detti anche
nodi) sono collegati tramite riferimenti
dall’uno all’altro
• Il caso più semplice di lista concatenata
prevede un collegamento unidirezionale
sequenziale a partire da un elemento iniziale
Fondamenti di Programmazione
Strutture dati + Generics
48
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Lista concatenata semplice
Il primo elemento viene detto “testa”
della lista ed è riferito da una variabile
che non è un elemento della lista
Questo riferimento è necessario per
accedere alla lista
Ogni elemento, tranne l’ultimo, riferisce
l’elemento successivo
Fondamenti di Programmazione
Lista concatenata doppia
Ogni elemento contiene due riferimenti (uno
all’elemento successivo, uno al precedente)
Il primo elemento contiene solo il riferimento al
successivo, l’ultimo elemento solo al precedente
Fondamenti di Programmazione
Strutture dati + Generics
49
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Lista circolare semplice
L’ultimo elemento è collegato al primo in modo da
chiudere circolarmente il collegamento. Le nozioni di
primo e ultimo elemento non sono più essenziali.
Rimane la necessità di un riferimento esterno dal quale
partire
Fondamenti di Programmazione
Lista circolare doppia
I riferimenti tra gli elementi realizzano un doppio anello
Fondamenti di Programmazione
Strutture dati + Generics
50
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Doppio riferimento
In tutte le varianti, oltre che il
riferimento alla testa si può
mantenere il riferimento anche
all’ultimo elemento (coda) della lista
Fondamenti di Programmazione
Strutture dati ricorsive
• Gli elementi di strutture dati concatenate sono
tipicamente definiti in modo ricorsivo:
ricorsivo: infatti ogni
elemento deve contenere un riferimento ad un elemento
dello stesso tipo
class NodoLista
{
private NodoLista prossimo;
. . .
}
class NodoListaDoppia
{
private NodoListaDoppia prossimo;
private NodoListaDoppia precedente;
. . .
}
Fondamenti di Programmazione
Strutture dati + Generics
51
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Generics e classi interne
• Per rappresentare una struttura dati sono tipicamente
necessarie almeno due classi: una che rappresenta la
struttura (p.e. la lista) “nel suo complesso” e una per
rappresentare i singoli elementi della lista
• Per definire strutture parametriche rispetto ai dati
contenuti negli elementi entrambe le classi devono essere
generiche (p.e. Lista<E>, ElementoLista<E>)
ElementoLista<E>)
• Si vuole però specificare che il tipo di una lista e dei suoi
elementi deve essere lo stesso. Questo non è possibile se
le due classi sono definite separatamente: bisogna invece
definire una classe internamente all’altra (inner
(inner class)
Fondamenti di Programmazione
Classi interne
• E’ possibile definire una classe all’interno di un’altra
• La classe interna non può essere public e
tipicamente sarà private (il suo uso è limitato alla
classe che la contiene)
• L’uso di classi interne (che non verrà approfondito) è
utile in situazioni molto specifiche
• Una di queste è la condivisione di una variabile di
tipo: la classe interna sarà definita in termini della
variabile di tipo della classe esterna
Fondamenti di Programmazione
Strutture dati + Generics
52
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Classi Lista e NodoLista
La classe Lista è parametrica
rispetto ad un tipo E
public class Lista<E>
In caso di lista semplice è sufficiente
{
il solo attributo testa che riferisce il
private NodoLista testa;
primo elemento della lista
private int numeroElementi;
numeroElementi;
. . .// METODI VARI DI Lista da approfondire dopo
La classe e i suoi attributi
private class NodoLista
sono private, ma visibili
{
all’interno della classe Lista
private E contenuto;
private NodoLista prossimo;
. . .// METODI DI NodoLista da approfondire dopo
}
La classe NodoLista è interna a Lista. Essa non è
esplicitamente generica ma la sua definizione può
}
contenere la variabile di tipo E della classe esterna.
Fondamenti di Programmazione
La classe NodoLista
private class NodoLista
{
private E contenuto;
private NodoLista prossimo;
public NodoLista (E _contenuto)
{
contenuto = _contenuto;
prossimo = null;
null;
}
La classe NodoLista ha due
semplici costruttori:
- uno per quando non c’è un
elemento successivo
- uno per quando c’è
public NodoLista (E _contenuto, NodoLista _prossimo)
{
contenuto = _contenuto;
prossimo = _prossimo;
}
}
Fondamenti di Programmazione
Strutture dati + Generics
53
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
La classe NodoLista
private class NodoLista
{. . .
public String toString()
toString()
{
return contenuto.toString
();
contenuto.toString();
}
La stringa descrittiva di un nodo coincide con la stringa
descrittiva del suo contenuto
}
Fondamenti di Programmazione
La classe Lista: costruttori
public class Lista<E>
{
private NodoLista testa;
private int numeroElementi;
numeroElementi;
public Lista()
{
testa = null;
null;
numeroElementi=0;
numeroElementi=0;
}
La classe Lista ha due semplici
costruttori:
- uno creare una lista vuota,
priva di elementi
- uno per quando il primo
elemento è specificato
public Lista(E primoElemento)
primoElemento)
{
testa = new NodoLista(
NodoLista(primoElemento);
primoElemento);
numeroElementi=1;
numeroElementi=1;
}
. . .
}
Fondamenti di Programmazione
Strutture dati + Generics
54
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
La classe Lista:
metodi di utilità
public class Lista<E>
{ . . .
public boolean isEmpty()
isEmpty()
{
return testa == null;
null;
}
La lista è vuota se il primo
riferimento è null
public String toString()
toString()
{
if (isEmpty())
isEmpty())
All’interno del metodo toString si ha il tipico
{
modo di scorrere tutti gli elementi di una
return MESS_VUOTA;
}
lista semplice
else
{
StringBuffer risultato = new StringBuffer();
StringBuffer();
NodoLista corrente = testa; Viene fissato un riferimento all’inizio della lista
while (corrente != null)
null)
La scansione termina quando il riferimento diventa null
{
risultato.append
(corrente.toString
toString()
() + "\
risultato.append(corrente.
"\n");
corrente = corrente.prossimo; L’avanzata avviene passando al prossimo elemento
}
return risultato.toString
();
risultato.toString();
}
}
Fondamenti di Programmazione
Scansione di una lista
testa
corrente
null
contenuto
contenuto
contenuto
contenuto
contenuto
next
next
next
next
next=null
Fondamenti di Programmazione
Strutture dati + Generics
55
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Inserimento in testa
public void inserimentoInTesta(E
inserimentoInTesta(E contenuto)
{
Il numero di elementi cresce e il nuovo
numeroElementi++;
numeroElementi++; NodoLista sarà riferito da testa in ogni caso
if (isEmpty())
isEmpty())
testa = new NodoLista(contenuto);
NodoLista(contenuto);
Se la lista è vuota, il nuovo nodo è anche l’unico e
avrà null come successore
else
testa = new NodoLista(contenuto,
NodoLista(contenuto, testa);
Altrimenti
avrà
come
successore quello che prima
}
era in testa
Fondamenti di Programmazione
Inserimento in coda
public void inserimentoInCoda(E
inserimentoInCoda(E contenuto)
Se la lista è vuota,
{
numeroElementi++;
numeroElementi++; testa e coda coincidono
if (isEmpty())
isEmpty())
testa = new NodoLista(contenuto);
NodoLista(contenuto);
else
Si porta il riferimento corrente all’ultimo elemento
{
NodoLista corrente = testa;
while(corrente.prossimo
while(corrente.prossimo != null)
null)
corrente=corrente.prossimo;
corrente.prossimo=new NodoLista(contenuto);
NodoLista(contenuto);
}
}
. . . e si aggiunge lì il nuovo nodo
Fondamenti di Programmazione
Strutture dati + Generics
56
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Inserimento in una posizione
public void inserimento(int
inserimento(int posizione, E contenuto) throws
ArrayIndexOutOfBoundsException
{
if (posizione < 0 || posizione > numeroElementi)
numeroElementi)
throw new ArrayIndexOutOfBoundsException();
ArrayIndexOutOfBoundsException();
Controllo che la posizione sia valida
if (posizione == 0)
inserimentoInTesta(contenuto);
inserimentoInTesta(contenuto);
else
Sappiamo già gestire le
if (posizione == numeroElementi)
numeroElementi)
posizioni estreme
inserimentoInCoda(contenuto);
inserimentoInCoda(contenuto);
else
{
Si porta il riferimento corrente
NodoLista corrente = testa;
all’elemento in posizione
for (int i=1; i < posizione; i++)
precedente a quello nuovo
corrente=corrente.prossimo;
corrente.prossimo=new NodoLista(contenuto,corrente.prossimo);
NodoLista(contenuto,corrente.prossimo);
}
numeroElementi++;
… e si aggiunge lì il nuovo nodo
numeroElementi++;
}
Fondamenti di Programmazione
Rimozione in testa
public E rimozioneInTesta()
rimozioneInTesta() throws
NoSuchElementException
Controllo che ci sia almeno un elemento da togliere
{
if (isEmpty())
isEmpty())
throw new NoSuchElementException();
NoSuchElementException();
E risultato = testa.contenuto; Metto via il risultato
testa=testa.prossimo; Spostando avanti il riferimento
ottengo un’eliminazione implicita
numeroElementi-;
numeroElementi--;
return risultato;
}
Fondamenti di Programmazione
Strutture dati + Generics
57
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Rimozione in coda
public E rimozioneInCoda()
throws NoSuchElementException
rimozioneInCoda()throws
{
Controllo che ci sia almeno un elemento da togliere
if (isEmpty())
isEmpty())
throw new NoSuchElementException();
NoSuchElementException();
Se l’elemento è uno solo so
if (numeroElementi == 1)
return rimozioneInTesta();
rimozioneInTesta(); già come fare
else
{ Porto il riferimento corrente sul penultimo elemento
NodoLista corrente = testa;
for (int i=1; i < numeroElementinumeroElementi-1; i++)
corrente=corrente.prossimo;
Metto via
E risultato = corrente.prossimo.contenuto; il risultato
corrente.prossimo = null;
null;
Mettendo a null il riferimento
numeroElementi-;
numeroElementi--;
ottengo un’eliminazione implicita
return risultato;
}
}
Fondamenti
di Programmazione
Rimozione in una posizione
public E rimozione (int
(int posizione) throws
ArrayIndexOutOfBoundsException
{
if (posizione < 0 || posizione > numeroElementinumeroElementi-1)
throw new ArrayIndexOutOfBoundsException();
ArrayIndexOutOfBoundsException();
Controllo che la posizione sia valida
if (posizione == 0)
return rimozioneInTesta();
rimozioneInTesta();
else
if (posizione == numeroElementinumeroElementi-1)
return rimozioneInCoda();
rimozioneInCoda();
else
{
NodoLista corrente = testa;
for (int i=1; i < posizione; i++)
corrente=corrente.prossimo;
Sappiamo già gestire le
posizioni estreme
Si porta il riferimento corrente
all’elemento in posizione
precedente a quello da eliminare
E risultato = corrente.prossimo.contenuto;
corrente.prossimo=corrente.prossimo.prossimo;
numeroElementi-;
numeroElementi--;
L’eliminazione si ottiene
return risultato;
}
scavalcamento
con uno
}
Fondamenti
di Programmazione
Strutture dati + Generics
58
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Alberi binari
• Un albero binario è un albero con la
caratteristica che ogni nodo può avere al più
due figli
• Gli alberi binari (in forme più sofisticate di
quella elementare che vedremo noi) sono
molto utilizzati per la gestione di dati dotati di
ordinamento, in quanto consentono
l’effettuazione efficiente di operazioni di
inserimento e ricerca
Fondamenti di Programmazione
Albero binario
Fondamenti di Programmazione
Strutture dati + Generics
59
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Albero binario
A
O
P
U
R
B E G
C
B C
A
G O P
U
R
P R U
G
A B
B
P
U
Fondamenti di Programmazione
Le classi BTree e TreeNode
La classe BTree è generica rispetto ad un tipo E che deve estendere l’interface
Comparable (deve essere possibile stabilire tra due elementi quale viene prima)
public class BTree <E extends Comparable<?
Comparable<? super E>>
{
private TreeNode root;
root;
. . .
private class TreeNode
{
private E value;
value;
private TreeNode left;
left;
private TreeNode right;
right;
. . .
}
Poiché l’interface Comparable è a
sua volta generica, si specifica il
relativo tipo. In pratica si indica
che E può estendere Comparable
direttamente o anche
ereditandola da una superclasse
Analogamente al caso
precedente, la classe BTree ha un
solo attributo per riferire la radice
dell’albero e contiene come classe
interna quella relativa ai nodi
Fondamenti di Programmazione
Strutture dati + Generics
60
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
La classe BTree
public class BTree <E extends Comparable<?
Comparable<? super E>>
{
private TreeNode root;
root;
public BTree()
BTree()
{
root = null;
null;
}
L’albero viene costruito
inizialmente vuoto
public void insertNode(E
insertNode(E newElement)
newElement)
{
Per l’inserimento di un nodo:
if ( root == null )
- se l’albero è vuoto si crea
root = new TreeNode(
TreeNode(newElement);
newElement);
semplicemente la radice
else
- altrimenti si invoca sulla
root.
root.insert(
insert(newElement);
newElement);
radice un metodo di
}
inserimento della classe
TreeNode
Fondamenti di Programmazione
La classe TreeNode
private class TreeNode
{
private E value;
value;
private TreeNode left;
left;
private TreeNode right;
right;
public TreeNode (E _value
)
_value)
{
value = _value
;
_value;
left = null;
null;
right = null;
null;
}
public String toString()
toString()
{
return value.
value.toString();
toString();
}
Fondamenti di Programmazione
Strutture dati + Generics
61
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Metodo di inserimento
"ordinato" in TreeNode
public void insert(E
insert(E newElement)
newElement)
Il nuovo elemento è
{
if (newElement.
newElement.compareTo(
compareTo(value)
value) < 0) minore del valore di questo
nodo: si va a sinistra
{
Se a sinistra non c’è nulla,
if (left == null)
null)
left = new TreeNode(
TreeNode(newElement);
newElement); si crea lì un nuovo nodo
else
Altrimenti si invoca ricorsivamente
left.
left.insert(
insert(newElement);
newElement);
il metodo sul figlio di sinistra
}
else
if (newElement.
newElement.compareTo(
compareTo(value)
value) > 0)Il caso di elemento
maggiore è del tutto
{
analogo
if (right == null)
null)
right = new TreeNode(
TreeNode(newElement);
newElement);
else
right.
right.insert(
insert( newElement );
Se newElement è un
}
doppione, viene ignorato
}
Fondamenti di Programmazione
Ricerca di un elemento
(nella classe BTree)
public boolean binarySearch(E
binarySearch(E toFind)
toFind)
Il metodo di ricerca
{
return searchHelper(
searchHelper(root,
root, toFind);
toFind); pubblico si avvale di un
metodo ricorsivo privato
}
private boolean searchHelper(
searchHelper(TreeNode node,
node, E wanted)
wanted)
{
Se nella ricerca si arriva a null,
if (node == null)
null) return false;l’elemento cercato non è presente
if (wanted.
wanted.compareTo(
compareTo(node.
node.value)
value) == 0)
return true;
true;
Qui abbiamo trovato l’elemento
if (wanted.
wanted.compareTo(
compareTo(node.
node.value)
value) < 0)
return searchHelper(
searchHelper(node.
node.left,
left, wanted);
wanted);
else
return searchHelper(
searchHelper(node.
node.right,
right, wanted);
wanted);
} La ricerca, se non è terminata, prosegue ricorsivamente a sinistra o a destra
Fondamenti di Programmazione
Strutture dati + Generics
62
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Traversata in ordine
public void inOrderTraversal()
inOrderTraversal()
{
inOrderHelper(
inOrderHelper(root);
root);
}
private void inOrderHelper(
inOrderHelper(TreeNode node)
node)
{
if (node == null)
null)
return;
inOrderHelper(
inOrderHelper(node.
node.left);
left);
System.out.println
(node.
System.out.println(
node.toString());
toString());
//o qualunque altra operazione
inOrderHelper(
inOrderHelper(node.
node.right);
right);
}
Fondamenti di Programmazione
Traversata in preordine
public void preOrderTraversal()
preOrderTraversal()
{
preOrderHelper(
preOrderHelper(root);
root);
}
private void preOrderHelper(
preOrderHelper(TreeNode node)
node)
{
if (node == null)
null)
return;
System.out.println
(node.
System.out.println(
node.toString());
toString());
//o qualunque altra operazione
preOrderHelper(
preOrderHelper(node.
node.left);
left);
preOrderHelper(
preOrderHelper(node.
node.right);
right);
}
Fondamenti di Programmazione
Strutture dati + Generics
63
Modulo di Fondamenti di Programmazione
P. Baroni - P.Martinelli - M. Rossi - A. Viola
Traversata in postordine
public void postOrderTraversal()
postOrderTraversal()
{
postOrderHelper(
postOrderHelper(root);
root);
}
private void postOrderHelper(
postOrderHelper(TreeNode node)
node)
{
if (node == null)
null)
return;
postOrderHelper(
postOrderHelper(node.
node.left);
left);
postOrderHelper(
postOrderHelper(node.
node.right);
right);
System.out.println
(node.
System.out.println(
node.toString());
toString());
//o qualunque altra operazione
}
Fondamenti di Programmazione
Strutture dati + Generics
64