Il tipo astratto di dati Node List Tipo di dati e operazioni Tipi di dati: oggetti arbitrari (come al solito) Operazioni: Metodi generici: - integer size() - boolean isEmpty() Metodi di accesso: - Position first(): Restituisce la posizione del primo elemento della lista - Position last(): Restituisce la posizione dell’ultimo elemento della lista - Position prev(Position p): Restituisce la posizione dell’ elemento che precede l’elemento in posizione p - Position next(Position p): Restituisce la posizione dell’ elemento che segue l’elemento in posizione p Strutture Dati Il tipo astratto di dati Node List Tipo di dati e operazioni Metodi di aggiornamento: - E set(Position p, E e): Rimpiazza l'elemento in posizione p con e, restituendo l'elemento che prima era in posizione p - addBefore (Position p, E e): Inserisce l’elemento e nella posizione che precede la posizione p - addAfter(Position p, E e): Inserisce l’elemento e nella posizione che segue la posizione p - addFirst(E e): Inserisce l’elemento e come primo elemento - addLast(E e): Inserisce l'elemento e come ultimo elemento - E remove(Position p): Rimuove e restituisce l’elemento in posizione p Strutture Dati Il tipo astratto di dati Node List Eccezioni InvalidPositionException Viene lanciata quando viene specificata come argomento una posizione non valida (ad esempio la posizione è null oppure è inesistente) BoundaryViolationException Viene lanciata quando si tenta di accedere ad una posizione al di fuori della lista (ad esempio si chiama il metodo next sull'ultima posizione della lista) EmptyListException Viene lanciata quando si invocano i metodi first() o last() su una lista vuota Strutture Dati Il tipo astratto di dati Node List L'interfaccia Position package position; public interface Position<E> { E element(); } Strutture Dati Il tipo astratto di dati Node List L'interfaccia PositionList public interface PositionList<E> { public int size(); public boolean isEmpty(); public Position<E> first() throws EmptyListException; public Position<E> last() throws EmptyListException; public Position<E> next(Position<E> p) throws InvalidPositionException, BoundaryViolationException; public Position<E> prev(Position<E> p) throws InvalidPositionException, BoundaryViolationException; public void addFirst(E e); public void addLast(E e); ... } Strutture Dati Il tipo astratto di dati Node List L'interfaccia PositionList public interface PositionList<E> { ... public void addAfter(Position<E> p, E e) throws InvalidPositionException; public void addBefore(Position<E> p, E e) throws InvalidPositionException; public E remove(Position<E> p) throws InvalidPositionException; public E set(Position<E> p, E e) throws InvalidPositionException; } Strutture Dati Il tipo astratto di dati Node List Implementazione basata su liste dopp. concatenate header Liste doppiam. concatenate NODI trailer ADT Lista POSIZIONI I nodi della lista doppiamente concatenata implementano le posizioni dell'ADT node list Strutture Dati Il tipo astratto di dati Node List Implementazione dell'interfaccia Position public class DNode<E> implements Position<E> { private DNode<E> prev, next; private E element; public DNode(DNode<E> newPrev, DNode<E> newNext, E elem) { prev = newPrev; next = newNext; element = elem; } public E element() { if ((prev == null) && (next == null)) throw new InvalidPositionException("La posizione non e` in una lista!"); return element; } public DNode<E> getNext() { return next; } public DNode<E> getPrev() { return prev; } public void setNext(DNode<E> newNext) { next = newNext; } public void setPrev(DNode<E> newPrev) { prev = newPrev; } public void setElement(E newElement) { element = newElement; } } Strutture Dati Il tipo astratto di dati Node List Implementazione dell'interfaccia PositionList import position.*; public class NodePositionList<E> implements PositionList<E> { protected int numElts; // Numero di elementi nella lista protected DNode<E> header, trailer; // Nodi sentinella public NodePositionList() { numElts = 0; header = new DNode<E>(null, null, null); trailer = new DNode<E>(header, null, null); header.setNext(trailer); } ... } Strutture Dati Il tipo astratto di dati Node List Implementazione dell'interfaccia PositionList public class NodePositionList<E> implements PositionList<E> { ... public int size() { return numElts; } public boolean isEmpty() { return (numElts == 0); } public Position<E> first() throws EmptyListException { if (isEmpty()) throw new EmptyListException("La lista e` vuota"); return header.getNext(); } public Position<E> last() throws EmptyListException { if (isEmpty()) throw new EmptyListException("La lista e` vuota"); return trailer.getPrev(); } ... } Strutture Dati Il tipo astratto di dati Node List Implementazione dell'interfaccia PositionList public class NodePositionList<E> implements PositionList<E> { ... protected DNode<E> checkPosition(Position<E> p) throws InvalidPositionException { if (p == null) throw new InvalidPositionException ("Null passato come posizione"); if (p == header) throw new InvalidPositionException ("header non e` una posizione valida di una lista"); if (p == trailer) throw new InvalidPositionException ("trailer non e` una posizione valida di una lista"); try { DNode<E> temp = (DNode<E>) p; if ((temp.getPrev() == null) || (temp.getNext() == null)) throw new InvalidPositionException ("La posizione non fa parte di una lista"); return temp; } catch (ClassCastException e) { throw new InvalidPositionException ("Il tipo della posizione non e` valido per questa lista"); } } ... } Strutture Dati Il tipo astratto di dati Node List Implementazione dell'interfaccia PositionList public class NodePositionList<E> implements PositionList<E> { ... public Position<E> prev(Position<E> p) throws InvalidPositionException, BoundaryViolationException { DNode<E> v = checkPosition(p); DNode<E> prev = v.getPrev(); if (prev == header) throw new BoundaryViolationException ("non posso retrocedere oltre l'inizio della lista"); return prev; } public Position<E> next(Position<E> p) throws InvalidPositionException, BoundaryViolationException { DNode<E> v = checkPosition(p); DNode<E> next = v.getNext(); if (next == trailer) throw new BoundaryViolationException ("non posso avanzare oltre la fine della lista"); return next; } ... } Strutture Dati Il tipo astratto di dati Node List Implementazione dell'interfaccia PositionList public class NodePositionList<E> implements PositionList<E> { ... public void addAfter(Position<E> p, E element) throws InvalidPositionException { DNode<E> v = checkPosition(p); numElts++; DNode<E> newNode = new DNode<E>(v, v.getNext(), element); v.getNext().setPrev(newNode); v.setNext(newNode); } public void addFirst(E element) { numElts++; DNode<E> newNode = new DNode<E>(header, header.getNext(), element); header.getNext().setPrev(newNode); header.setNext(newNode); } ... } Strutture Dati Il tipo astratto di dati Node List Implementazione dell'interfaccia PositionList public class NodePositionList<E> implements PositionList<E> { ... public E remove(Position<E> p) throws InvalidPositionException { DNode<E> v = checkPosition(p); numElts--; DNode<E> vPrev = v.getPrev(); DNode<E> vNext = v.getNext(); vPrev.setNext(vNext); vNext.setPrev(vPrev); E vElem = v.element(); // scollega la posizione dalla lista rendendola invalida v.setNext(null); v.setPrev(null); return vElem; } ... } Strutture Dati Esercizio Merge sort L'algoritmo merge sort è un algoritmo di ordinamento che ordina una sequenza di elementi ricorsivamente nel modo seguente: 1. divide la sequenza in due sequenze non ordinate, ciascuna avente taglia circa la metà della sequenza originale; 2. ordina separatamente ciascuna delle due sequenze; 3. fonde le due sequenze ordinate in un'unica sequenza ordinata. Dare un'implementazione del merge sort usando l'ADT Lista. Si assuma che gli elementi da ordinare siano di tipo Integer. Strutture Dati Soluzione Merge sort public static void mergeSort(PositionList<Integer> L){ int n = L.size(); if (n < 2) return; PositionList<Integer> L1 = new NodePositionList<Integer>(); PositionList<Integer> L2 = new NodePositionList<Integer>(); for (int i = 0; i < n/2; i++) L1.addLast(L.remove(L.first())); for (int i = n/2; i < n; i++) L2.addLast(L.remove(L.first())); mergeSort(L1); mergeSort(L2); merge(L1,L2,L); } Strutture Dati Soluzione Merge sort public static void merge(PositionList<Integer> L1, PositionList<Integer> L2, PositionList<Integer> L){ while (!L1.isEmpty() && !L2.isEmpty()) if (L1.first().element() <= L2.first().element()) L.addLast(L1.remove(L1.first())); else L.addLast(L2.remove(L2.first())); while(!L1.isEmpty()) // sposta i rimanenti elementi di L1 L.addLast(L1.remove(L1.first())); while(!L2.isEmpty()) // sposta i rimanenti elementi di L2 L.addLast(L2.remove(L2.first())); } Strutture Dati Iteratore A cosa serve Una operazione molto frequente sugli ADT array list e lista di nodi è quella di scandire la collezione di elementi un elemento alla volta Esempi: - cercare uno specifico elemento - visualizzare ordinatamente gli elementi della collezione - determinare il numero di occorrenze di un dato elemento - sommare tutti gli elementi di una collezione Strutture Dati Iteratore A cosa serve Un iteratore è un software design pattern che permette la scansione di un qualsiasi ADT che astrae una collezione ordinata di elementi Un iteratore può essere visto come una estensione del concetto di posizione (position): - l'ADT position incapsula il concetto di “posto” - un iteratore incapsula il concetto di “posto” e di “posto successivo” Strutture Dati Iteratore Esempio Data una collezione ordinata di oggetti (array list, node list) A Strutture Dati B C D Iteratore Esempio Data una collezione ordinata di oggetti (array list, node list) A B C D un iteratore è un nuovo ADT che ci permette di scandire la collezione Strutture Dati Iteratore Esempio Data una collezione ordinata di oggetti (array list, node list) A B C D ADT iteratore A un iteratore è un nuovo ADT che ci permette di scandire la collezione Strutture Dati Iteratore Esempio Data una collezione ordinata di oggetti (array list, node list) A B C D ADT iteratore B un iteratore è un nuovo ADT che ci permette di scandire la collezione Strutture Dati Iteratore Esempio Data una collezione ordinata di oggetti (array list, node list) A B C D ADT iteratore C un iteratore è un nuovo ADT che ci permette di scandire la collezione Strutture Dati Iteratore Esempio Data una collezione ordinata di oggetti (array list, node list) A B C D ADT iteratore D un iteratore è un nuovo ADT che ci permette di scandire la collezione Strutture Dati Iteratore Definizione del tipo astratto di dati Un iteratore può essere definito come un ADT che supporta i seguenti due metodi: - public boolean hasNext(): determina se ci sono altri elementi nell'iteratore - public E next(): restituisce l'elemento successivo in un iteratore Strutture Dati Iteratore Definizione del tipo astratto di dati Un iteratore può essere definito come un ADT che supporta i seguenti due metodi: - public boolean hasNext(): determina se ci sono altri elementi nell'iteratore - public E next(): restituisce l'elemento successivo in un iteratore eccezione: NoSuchElementException quando next() viene invocato su una collezione vuota o sull'ultimo elemento della collezione Strutture Dati Iteratore Definizione del tipo astratto di dati Un iteratore può essere definito come un ADT che supporta i seguenti due metodi: - public boolean hasNext(): determina se ci sono altri elementi nell'iteratore - public E next(): restituisce l'elemento successivo in un iteratore eccezione: NoSuchElementException quando next() viene invocato su una collezione vuota o sull'ultimo elemento della collezione java fornisce un iteratore con l'interfaccia java.util.Iterator Strutture Dati Iteratore Implementazione un iteratore viene solitamente associato ad uno degli ADT ad accesso sequenziale (array list, lista di nodi) un iteratore permette di accedere a tutti gli oggetti della collezione nell'ordine lineare stabilito il primo elemento di un iteratore si ottiene dalla prima chiamata del metodo next() (assumendo che l'iteratore contenga almeno un elemento) un iteratore deve essere indipendente dal modo con cui è organizzata la collezione di oggetti a cui è associato Strutture Dati Iteratore Implementazione Vogliamo un meccanismo uniforme per scandire una struttura dati ad accesso sequenziale: dobbiamo fare in modo che tutti i tipi astratti di dati ad accesso sequenziale supportino il seguente metodo: Iterator<E> iterator(): restituisce un iteratore degli elementi della collezione Strutture Dati Iteratore Implementazione Vogliamo un meccanismo uniforme per scandire una struttura dati ad accesso sequenziale: dobbiamo fare in modo che tutti i tipi astratti di dati ad accesso sequenziale supportino il seguente metodo: Iterator<E> iterator(): restituisce un iteratore degli elementi della collezione in Java 5 questo metodo è fornito dall'interfaccia java.lang.Iterable Strutture Dati Iteratore Implementazione La nostra implementazione deve consentire di tener traccia del punto della collezione indicato dal cursore dell'iteratore creare un nuovo iteratore consiste semplicemente nel creare un oggetto iteratore che rappresenta un cursore posto prima del primo elemento della collezione usando la prima volta il metodo next(), si sposta il cursore sul primo elemento della collezione successive invocazioni del metodo next() ci permetteranno di scandire tutti gli elementi della collezione il metodo hasNext() ci permette di capire quando abbiamo finito di scandire tutta la collezione (va usato prima di invocare next()) Strutture Dati Iteratore (lista di nodi) Modifica dell'interfaccia PositionList<E> Aggiungiamo il metodo iterator() all'interfaccia PositionList<E>: public interface PositionList<E> { ... } Strutture Dati Iteratore (lista di nodi) Modifica dell'interfaccia PositionList<E> Aggiungiamo il metodo iterator() all'interfaccia PositionList<E>: import java.util.Iterator; public interface PositionList<E> extends Iterable<E>{ ... public Iterator<E> iterator(); } Nelle prossime diapositive vediamo come implementare questo metodo Strutture Dati Iteratore (lista di nodi) La classe ElementIterator<E> import java.util.Iterator; import position.*; public class ElementIterator<E> implements Iterator<E>{ protected PositionList<E> list; // la lista su cui implem. l'iteratore protected Position<E> cursor; // indica la successiva posizione public ElementIterator(PositionList<E> L) { list = L; cursor = (list.isEmpty())? null : list.first(); } public boolean hasNext() { return (cursor != null); } public E next() throws NoSuchElementException { if (cursor == null) throw new NoSuchElementException("No next element"); E toReturn = cursor.element(); cursor = (cursor == list.last())? null : list.next(cursor); return toReturn; } public void remove() { } } Strutture Dati Iteratore (lista di nodi) Il metodo iterator() nella classe NodePositionList Aggiungiamo l'implementazione del metodo iterator() (che usa la classe ElementIterator<E>) alla classe NodePositionList<E> che implementa l'interfaccia PositionList<E>: public class NodePositionList<E> implements PositionList<E> { ... public Iterator<E> iterator() {return new ElementIterator<E>(this);} } Strutture Dati Merge sort Scansione della lista con un iteratore public static void main(String[] args) { PositionList<Integer> L1 = new NodePositionList<Integer>(); L1.addLast(7); L1.addLast(5); L1.addLast(2); L1.addLast(4); L1.addLast(1); L1.addLast(8); mergeSort(L1); Iterator<Integer> I = L1.iterator(); while (I.hasNext()) System.out.print(I.next()+" "); } Strutture Dati Esercizio di test Scrivere un metodo public static void remove (PositionList<Integer> L, Integer i) che rimuova da una lista L di interi tutte le occorrenze dell'intero i. Strutture Dati Iteratore sulle posizioni Per gli ADT con Position Negli ADT che supportano la nozione di position (come la lista di nodi) possiamo fare una scansione delle posizioni oltre che degli elementi? Strutture Dati Iteratore sulle posizioni Per gli ADT con Position Negli ADT che supportano la nozione di position (come la lista di nodi) possiamo fare una scansione delle posizioni oltre che degli elementi? Possiamo fornire anche il metodo: positions(): restituisce un oggetto Iterable contenente le posizioni della collezione come suoi elementi Strutture Dati Iteratore sulle posizioni Per gli ADT con Position Aggiungiamo il metodo positions() all'interfaccia PositionList<E>: import java.util.Iterator; public interface PositionList<E> extends Iterable<E>{ ... public Iterator<E> iterator(); public Iterable<Position<E>> positions(); } Strutture Dati Iteratore sulle posizioni (nodelist) Implementazione del metodo positions() public class NodePositionList<E> implements PositionList<E> { ... public Iterable<Position<E>> positions() {// crea una lista di posizioni PositionList<Position<E>> P = new NodePositionList<Position<E>>(); if (!isEmpty()) { Position<E> p = first(); while (true) { P.addLast(p); // aggiunge la posizione p come ultimo elemento di P if (p == last()) break; p = next(p); } } return P; // restituisce P come oggetto Iterable } public Iterator<E> iterator() {return new ElementIterator<E>(this);} } Strutture Dati Iteratore sulle posizioni (nodelist) Implementazione del metodo positions() Cosa fa il metodo positions()? Il metodo positions() se invocato su una lista L crea una nuova lista P contenente le posizioni di L ● ● La lista P viene restituita come un oggetto Iterable<Position<E>> Ciò ci permette di invocare il metodo iterator() su P per ottenere facilmente un iteratore sulle posizioni della lista originale L Strutture Dati Ritorniamo al nostro esercizio Uso del metodo positions() Scrivere un metodo public static void remove (PositionList<Integer> L, Integer i) che rimuova da una lista L di interi tutte le occorrenze dell'intero i. Strutture Dati Ritorniamo al nostro esercizio Soluzione public static void remove(PositionList<Integer> L, Integer i){ Iterable<Position<Integer>> It = L.positions(); Iterator<Position<Integer>> I = It.iterator(); while (I.hasNext()){ Position<Integer> p = I.next(); if (p.element().equals(i)) L.remove(p); } } public static void main(String[] args) { PositionList<Integer> L1 = new NodePositionList<Integer>(); ... remove(L1,1); Iterator<Integer> I = L1.iterator(); while (I.hasNext()) System.out.print(I.next()+" "); } } Strutture Dati For-each-loop for(Iterator<Type> iter = expression.iterator(); iter.hasNext();){ Type name = iter.next(); loop_statement } for(Type name : expression){ loop_statement } Strutture Dati For-each-loop for(Iterator<Type> iter = expression.iterator(); iter.hasNext();){ Type name = iter.next(); loop_statement } for(Iterator<Position<E>> iter = positions.iterator(); iter.hasNext();){ Position<E> pos = iter.next(); System.out.println(pos.element()); } for(Type name : expression){ loop_statement } Strutture Dati For-each-loop for(Iterator<Type> iter = expression.iterator(); iter.hasNext();){ Type name = iter.next(); loop_statement } for(Iterator<Position<E>> iter = positions.iterator(); iter.hasNext();){ Position<E> pos = iter.next(); System.out.println(pos.element()); } for (Position<E> pos: positions) System.out.println(pos.element()); for(Type name : expression){ loop_statement } Strutture Dati For-each-loop Attenzione: per poter usare il for-each-loop è necessario utilizzare le interfacce java.lang.Iterable e java.util.Iterator Strutture Dati Ritorniamo al nostro esercizio Uso del metodo positions() e del for-each-loop Scrivere un metodo public static void remove (PositionList<Integer> L, Integer i) che rimuova da una lista L di interi tutte le occorrenze dell'intero i. Strutture Dati Ritorniamo al nostro esercizio Soluzione con il for-each-loop public static void remove(PositionList<Integer> L, Integer i){ for (Position<Integer> p : L.positions()) if (p.element().equals(i)) L.remove(p); } public static void main(String[] args) { PositionList<Integer> L1 = new NodePositionList<Integer>(); ... remove(L1,1); for (Integer i : L1) System.out.print(i+" "); } Strutture Dati