![]() |
NFP121 |
|
Les TP Hebdomadaires |
Reprendre
le TD sur les listes pointées : on demandait d'implanter les listes
pointées de 3 manières différentes pour un seul comportement
commun.
on obient le diagramme de classes suivant :
QUESTION : justifier la présence de LPAbstraite...
protected LinkedList liste= new LinkedList(); public LP_Java(){ super(); } public Object car(){ return liste.getFirst(); } public void cdr() { liste.removeFirst(); } public void cons(Object objet){ liste.addFirst(objet); } public void conc(LPointeeI li ){ // for(Iterator it = li.iterator();it.hasNext(); ) this.liste.add(it.next()); for(Object elem : li){ liste.add(elem); } } public boolean membre(Object objet){ return liste.contains(objet); } public int longueur(){ return liste.size(); } public boolean listeVide(){ return liste.isEmpty(); } public Iterator iterator(){ return this.liste.iterator(); } public String toString(){ return liste.toString(); } |
Les éléments de telles listes sont donc de classe Object i.e. on peut mettre tout et n'importe quoi dans de telles listes.
Un premier moyen d'obtenir des listes homogènes (dont tous les éléments sont de même classe)est donné dans le cours n°3 Transparents 29-30-31 . Dans le cas le plus simple (Transparents 29-30) on devra développer une nouvelle classe pour chaque classe d'éléments désirés. d'où multiplication des classes... on obtient des situations (incomplète sur le diagramme) comme :
La généricité doit nous éviter d'avoir à développeer toutes ces classes particulières. Le diagrammes de classes est maintenant :
public interface LPointeeI<T> extends Iterable<T>{ /** * retourne la valeur du premier élément de la liste sans destruction */ public T car(); /** * supprime la tete de la liste * remarque : on ne peut pas supprimer la tête d'une liste vide */ public void cdr() throws ListeVideException; /** * ajoute 'obj' au début de la liste; * la longueur de la liste est augmentée de 1 */ public void cons(T objet); /** * ajoute en fin de liste la liste 'liste' : c'est la concaténation */ public void conc(LPointeeI<T> liste ); /** * retourne la liste */ public boolean membre(T objet); /** * */ public boolean listeVide(); /** * retourne le nombre d'éléments dans la liste */ public int longueur(); } |
code pour LP_Java |
import java.util.*; /** * Décrivez votre classe ListeP_Java ici. * * @author (votre nom) * @version (un numéro de version ou une date) */ public class LP_Java<T> extends LPAbstraite<T> { protected LinkedList<T> liste= new LinkedList<T>(); public LP_Java(){ super(); } public T car(){ return liste.getFirst(); } public void cdr() { liste.removeFirst(); } public void cons(T objet){ liste.addFirst(objet); } public void conc(LPointeeI<T> li ){ for(T elem : li){ liste.add(elem); } } public boolean membre(T objet){ return liste.contains(objet); } public int longueur(){ return liste.size(); } public boolean listeVide(){ return liste.isEmpty(); } public Iterator<T> iterator(){ return this.liste.iterator(); } public String toString(){ return liste.toString(); } } |
la transformation de LP_Java en LP_Java<T> est la plus simple puisqu'on s'appuie sur la classe LinkedList<T> déjà générique...
Question développer les deux autres implémentations génériques (on ne doit plus avoir aucun warning à la compilation).
Question ajouter pour chacune des implantations la méthode equals de telle manière qu'on puisse comparer deux listes pointées sans se préoccuper de leur implantation.
rappel : deux listes pointées sont égales si elles ont le même nombre d'éléments et que leurs éléments de même rangs sont égaux.
nous limitons maintenant à l'implantation LP_Java...
Question : developper une méthode sort de tri selon "l'ordre naturel" de la liste puis une méthode de tri où on passe en paramètre le relation d'ordre à prendre en compte.
remarque : étudier les méthodes sort de la classe boite à outils Collections. quel le problème posé par la classe PolyReg ? on dira qu'un polygone est plus petit qu'un autre si il a moins de cotés et à nombre de cotés égal si la longueur du coté est plus faible.
à suivre...
à suivre...
1/Idée des collections : "traiter de manière uniforme tous les agrégats d'éléments divers et variés
Collections de base :
Mais aussi les interface Enumeration , Iterator , ListIterator , ...
public interface Iterator { public abstract boolean hasNext(); public abstract Object next(); public abstract void remove(); } |
public interface ListIterator implements Iterator { public abstract boolean hasNext(); public abstract Object next(); public abstract boolean hasPrevious(); public abstract Object previous(); public abstract int nextIndex(); public abstract int previousIndex(); public abstract void remove(); public abstract void set(Object o); public abstract void add(Object o); } |
Remarques : les collections fournissent des Classes
(complétement implantées) facilitant
le développement en évitant beaucoup de réécriture
: AbstractCollection, AbstractList,
AbstractSet, ArrayList, BeanContextServicesSupport,
BeanContextSupport, HashSet, LinkedHashSet, LinkedList, TreeSet, Vector,
Stack, ...
ATTENTION : pour implanter par exemple ses propres vecteurs avec son propre
jargon (en français par exemple ...)
On préférera toujours "l'utilisation" d'une classe existante
à l'héritage
public class Vecteur {
private Vector contenant;
....
}
plutot que
public class Vecteur extends Vector ...
QUESTION : quelle justification ?
généricité (coté List...) :
EXO : les "pures interfaces" avec <A> un type variable et retrouver les interfaces classiques, après "ERASEment" ou "gommage"
Tree<T> ==> Tree
Tree<Integer> ==> Tree T (in class Tree) ==>Object |
public interface Collection<A> { public abstract int size(); public abstract boolean isEmpty(); public abstract boolean contains(A o); public abstract Iterator<A> iterator(); public abstract Object[] toArray(); // pourquoi Object ici et NON pas A[] ??? public abstract A[] toArray(A[] a); public abstract boolean add(A o); public abstract boolean remove(A o); public abstract boolean containsAll(Collection<A> c); public abstract boolean addAll(Collection<A> c); public abstract boolean removeAll(Collection<A> c); public abstract boolean retainAll(Collection<A> c); public abstract void clear(); public abstract boolean equals(Object o); public abstract int hashCode(); }
Rappels : A List is an ordered Collection(sometimes called a sequence).
Lists may contain duplicate elements. In addition to the
operations inherited from Collection,
the List interface includes operations for:
Positional Access: manipulate elements based on their numerical position in the list.
Search: search for a specified object in the list and return its numerical position.
List Iteration: extend Iterator semantics to take advantage of the list's sequential nature.
Range-view : perform arbitrary range operations on the list.
public interface List<A> implements Collection<A>{ public abstract int size(); public abstract boolean isEmpty(); public abstract boolean contains(A o); public abstract Iterator<A> iterator(); public abstract Object[] toArray(); public abstract boolean add(A o); public abstract boolean remove(A o); public abstract boolean containsAll(Collection<A> c); public abstract boolean addAll(Collection<A> c); public abstract boolean removeAll(Collection<A> c); public abstract boolean retainAll(Collection<A> c); public abstract void clear(); public abstract boolean equals(Object o); public abstract int hashCode(); public abstract A get(int index); public abstract A set(int index, A element); public abstract void add(int index, A element); public abstract A remove(int index); public abstract int indexOf(A o); public abstract int indexOf(A o,int index); public abstract int lastIndexOf(A o); public abstract int lastIndexOf(A o,int index); public abstract void removeRange(int fromIndex, int toIndex); public abstract boolean addAll(int index, Collection<A> c); public abstract ListIterator<A> listIterator(); public abstract ListIterator<A> listIterator(int index); }
public abstract class AbstractList<A> extends AbstractCollection<A> implements List<A> { public boolean add(A o); public abstract A get(int index); // seule mémthode abstract public A set(int index, A element); public void add(int index, A element); public A remove(int index); public int indexOf(A o); public int indexOf(A o, int index); public int lastIndexOf(A o); public int lastIndexOf(A o, int index); public void removeRange(int fromIndex, int toIndex); public boolean addAll(int index, Collection<A> c); public Iterator<A> iterator(); public ListIterator<A> listIterator(); public ListIterator<A> listIterator(int index); public boolean equals(Object o); // constructeur public AbstractList(); }
On a alors retrouvé toutes les dépendances nécessaires à l'implantation de Vector dont la "pure interface "est :
public class Vector extends AbstractList implements List, RandomAccess, Cloneable, Serializable{ void add(int index, Object element) boolean add(Object o) boolean addAll(Collection c) boolean addAll(int index, Collection c) void addElement(Object obj) int capacity() void clear() Object clone() boolean contains(Object elem) boolean containsAll(Collection c) void copyInto(Object[] anArray) Object elementAt(int index) Enumeration elements() void ensureCapacity(int minCapacity) boolean equals(Object o) Object firstElement() Object get(int index) int hashCode() int indexOf(Object elem) int indexOf(Object elem, int index) void insertElementAt(Object obj, int index) boolean isEmpty() Object lastElement() int lastIndexOf(Object elem) int lastIndexOf(Object elem, int index) Object remove(int index) boolean remove(Object o) boolean removeAll(Collection c) void removeAllElements() boolean removeElement(Object obj) void removeElementAt(int index) protected void removeRange(int fromIndex, int toIndex) boolean retainAll(Collection c) Object set(int index, Object element) void setElementAt(Object obj, int index) void setSize(int newSize) int size() List subList(int fromIndex, int toIndex) Object[] toArray() Object[] toArray(Object[] a) String toString() void trimToSize() } |
On veut implanter des vector d'Integer , de String , de Boolean
, de Character , etc ...
Puis des Stack d'Integer , de String , de Boolean , de Character , etc
...
Première solution : IntegerVector extends Vector et par exemple :
import java.util.Vector; import java.util.Collection; /** * Write a description of class IntegerVector here. * * @author (your name) * @version (a version number or a date) */ public class IntegerVector extends Vector { public IntegerVector(){ super(); } public IntegerVector(Collection c){ super((IntegerVector)c); } public IntegerVector(int initialCapacity){ super(initialCapacity); } public IntegerVector(int initialCapacity, int capacityIncrement){ super(initialCapacity, capacityIncrement); } public void add(int index, Object element){ super.add(index , (Integer)element); } public boolean add(Object o){ return super.add((Integer)o); } public boolean addAll(Collection c){ return super.addAll((IntegerVector)c); } public boolean addAll(int index, Collection c){ return super.addAll(index , (IntegerVector)c); } public void addElement(Object obj){ super.addElement((Integer)obj); } public boolean contains(Object elem){ return super.contains((Integer)elem); } public boolean containsAll(Collection c){ return super.containsAll((IntegerVector)c); } /* void copyInto(Object[] anArray) Object elementAt(int index) Enumeration elements() boolean equals(Object o) */ public Object firstElement(){ return (Integer)super.firstElement(); } public Object get(int index){ return (Integer)super.get(index); } public int indexOf(Object elem){ return super.indexOf((Integer)elem); } public int indexOf(Object elem, int index){ return super.indexOf((Integer)elem , index); } public void insertElementAt(Object obj, int index){ super.insertElementAt((Integer)obj , index); } public Object lastElement(){ return (Integer)super.lastElement(); } public int lastIndexOf(Object elem){ return super.lastIndexOf((Integer)elem); } public int lastIndexOf(Object elem, int index){ return super.lastIndexOf((Integer)elem , index); } public boolean remove(Object o){ return super.remove((Integer)o); } // Object remove(int index) public boolean removeAll(Collection c){ return super.removeAll((IntegerVector)c); } public boolean removeElement(Object obj){ return super.removeElement((Integer)obj); } public boolean retainAll(Collection c){ return super.retainAll((IntegerVector)c); } public void setElementAt(Object obj, int index){ super.setElementAt((Integer)obj , index); } /* Object[] toArray() Object[] toArray(Object[] a) */ } |
Donc des 'Cast' pour controler l'utilisation exclusive de Integer... on fera de même pour StringVector , ...
Mais si on veut maintenant créer des Stack sur le même thème il faut choisir entre
IntegerStack extends Stack ... cf ci dessus mais on peut utiliser toutes les méthodes de Vector "standard"
et
IntegerStack extends IntegerVector mais il faut rédéfinir effectivement les méthodes de Stack
pour la deuxième solution par exemple :
public class IntegerStack extends IntegerVector{ public IntegerStack(){ super(); } public Object peek(){ return (Integer)super.lastElement(); } public Object pop(){ Integer ii=(Integer)super.lastElement(); removeElementAt(super.size()-1); return ii; } public Object push(Object item){ super.add((Integer)item); return (Integer)item; } public int search(Object o){ return super.lastIndexOf((Integer)o)+1; } } |
QUESTION : construire un programme de test et rappeler que les erreurs seront toutes "at runtime"
exemple :
public class Test_A_Vecteurs{ public static void main(String args[]){ Vector v=new IntegerVector(); v.add(new Integer(66)); v.add(new Integer(6)); //v.add("six"); v.add(new Integer(12)); //v.add("douze"); System.out.println("le vecteur v : " + v.toString()); Vector v2=(IntegerVector)v.clone(); System.out.println("le vecteur v2(=v;clone()) : " + v2.toString()); v.addAll(v2); System.out.println("le vecteur v2 après v.addAll(v2)...: " + v2.toString()); System.out.println("le vecteur v + v2 : " + v.toString()); System.out.println("6 dans v ? : " + v.contains(new Integer(6))); //System.out.println("six dans v ? : " + v.contains("six")); //ClassCastException at runtime.... rien à la compilation System.out.println("v2 dans v ? : " + v.containsAll(v2)); //Integer[] tv={new Integer(123) , "douze" , new Integer(456)}; // Erreur de Compilation Object[] tv=new Integer[12]; v.copyInto(tv); System.out.println("le tableau tv : "); for(int i=0 ; i<v.size() ; i++){ System.out.print(tv[i] + " "); } System.out.println(""); int ii=v.size(); for(int i=0 ; i<ii ; i++){ v.add(tv[i]); } System.out.println("v + le tableau tv : " + v); System.out.println("v retain v2 : " + v.retainAll(v2)); v.add(new Integer(18)); v.removeAll(v2); System.out.println("v + removeAll v2 " + v); //v.remove("six");// ClasCastException at runtime... //v.lastIndexOf("six");// ClasCastException at runtime... //v.insertElementAt("six",1);// ClasCastException at runtime... // maintenant la pile : ATTENTION pas de déclaration plus générale!!! IntegerStack p=new IntegerStack(); System.out.println("p pile de départ : " + p); Object o=new Integer(24); p.push(o); System.out.println("p pile +1 " + p); } } |
mais ensuite il faut autant de tels couples de classes :
Voyons la version générique (GJ) :
import java.util.*; public class Vector<A> extends AbstractList<A> implements List<A>, Cloneable, java.io.Serializable { protected A[] elementData; protected int elementCount; protected int capacityIncrement; private static final long serialVersionUID = -2767605614048989439L; public Vector(int initialCapacity, int capacityIncrement) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new A[initialCapacity]; this.capacityIncrement = capacityIncrement; } public Vector(int initialCapacity) { this(initialCapacity, 0); } public Vector() { this(10); } public Vector(Collection<A> c) { this((c.size()*110)/100); // Allow 10% room for growth Iterator<A> i = c.iterator(); while (i.hasNext()) elementData[elementCount++] = i.next(); } public synchronized void copyInto(A anArray[]) { System.arraycopy(elementData, 0, anArray, 0, elementCount); } public synchronized void trimToSize() { modCount++; int oldCapacity = elementData.length; if (elementCount < oldCapacity) { A oldData[] = elementData; elementData = new A[elementCount]; System.arraycopy(oldData, 0, elementData, 0, elementCount); } } public synchronized void ensureCapacity(int minCapacity) { ensureCapacityHelper(minCapacity); } private void ensureCapacityHelper(int minCapacity) { modCount++; int oldCapacity = elementData.length; if (minCapacity > oldCapacity) { A oldData[] = elementData; int newCapacity = (capacityIncrement > 0) ? (oldCapacity + capacityIncrement) : (oldCapacity * 2); if (newCapacity < minCapacity) { newCapacity = minCapacity; } elementData = new A[newCapacity]; System.arraycopy(oldData, 0, elementData, 0, elementCount); } } public synchronized void setSize(int newSize) { modCount++; if (newSize > elementCount) { ensureCapacityHelper(newSize); } else { for (int i = newSize ; i < elementCount ; i++) { elementData[i] = null; } } elementCount = newSize; } public int capacity() { return elementData.length; } public int size() { return elementCount; } public boolean isEmpty() { return elementCount == 0; } public synchronized Enumeration<A> elements() { return new Enumeration<A>() { int count = 0; public boolean hasMoreElements() { return count < elementCount; } public A nextElement() { synchronized (Vector.this) { if (count < elementCount) { return elementData[count++]; } } throw new NoSuchElementException("Vector Enumeration"); } }; } /* public boolean contains(A elem) { return indexOf(elem, 0) >= 0; } */ /* public int indexOf(A elem) { return indexOf(elem, 0); } */ public synchronized int indexOf(A elem, int index) { if (elem == null) { for (int i = index ; i < elementCount ; i++) if (elementData[i]==null) return i; } else { for (int i = index ; i < elementCount ; i++) if (elem.equals(elementData[i])) return i; } return -1; } /* public int lastIndexOf(A elem) { return lastIndexOf(elem, elementCount-1); } */ public synchronized int lastIndexOf(A elem, int index) { if (elem == null) { for (int i = index; i >= 0; i--) if (elementData[i]==null) return i; } else { for (int i = index; i >= 0; i--) if (elem.equals(elementData[i])) return i; } return -1; } public synchronized A elementAt(int index) { if (index >= elementCount) { throw new ArrayIndexOutOfBoundsException(index + ">=" + elementCount); } try { return elementData[index]; } catch (ArrayIndexOutOfBoundsException e) { throw new ArrayIndexOutOfBoundsException(index + " < 0"); } } public synchronized A firstElement() { if (elementCount == 0) { throw new NoSuchElementException(); } return elementData[0]; } public synchronized A lastElement() { if (elementCount == 0) { throw new NoSuchElementException(); } return elementData[elementCount - 1]; } public synchronized void setElementAt(A obj, int index) { if (index >= elementCount) { throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount); } elementData[index] = obj; } public synchronized void removeElementAt(int index) { modCount++; if (index >= elementCount) { throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount); } else if (index < 0) { throw new ArrayIndexOutOfBoundsException(index); } int j = elementCount - index - 1; if (j > 0) { System.arraycopy(elementData, index + 1, elementData, index, j); } elementCount--; elementData[elementCount] = null; /* to let gc do its work */ } public synchronized void insertElementAt(A obj, int index) { modCount++; if (index >= elementCount + 1) { throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount); } ensureCapacityHelper(elementCount + 1); System.arraycopy(elementData, index, elementData, index + 1, elementCount - index); elementData[index] = obj; elementCount++; } public synchronized void addElement(A obj) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = obj; } public synchronized boolean removeElement(A obj) { modCount++; int i = indexOf(obj); if (i >= 0) { removeElementAt(i); return true; } return false; } public synchronized void removeAllElements() { modCount++; for (int i = 0; i < elementCount; i++) { elementData[i] = null; } elementCount = 0; } public synchronized Object clone() { // try { Vector<A> v = new Vector<A>(); v.addAll(this); return v; // } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable // throw new InternalError(); // } } public synchronized A[] toArray() { A[] result = new A[elementCount]; System.arraycopy(elementData, 0, result, 0, elementCount); return result; } public synchronized A get(int index) { if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); return elementData[index]; } public synchronized A set(int index, A element) { if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); A oldValue = elementData[index]; elementData[index] = element; return oldValue; } public synchronized void add(int index, A element) { if (index > elementCount) throw new ArrayIndexOutOfBoundsException(index); ensureCapacityHelper(elementCount+1); // Increments modCount System.arraycopy(elementData, index, elementData, index + 1, elementCount - index); elementData[index] = element; elementCount++; } public synchronized A remove(int index) { modCount++; if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); A oldValue = elementData[index]; int numMoved = elementCount - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--elementCount] = null; // Let gc do its work return oldValue; } public synchronized void clear() { modCount++; // Let gc do its work for (int i = 0; i < elementCount; i++) elementData[i] = null; elementCount = 0; } /* public synchronized boolean addAll(Collection<A> c) { modCount++; int numNew = c.size(); ensureCapacityHelper(elementCount + numNew); Iterator<A> e = c.iterator(); for (int i=0; i<numNew; i++) elementData[elementCount++] = e.next(); return numNew != 0; } */ public synchronized void removeRange(int fromIndex, int toIndex) { modCount++; if (fromIndex < 0 || fromIndex >= elementCount || toIndex > elementCount || toIndex < fromIndex) throw new ArrayIndexOutOfBoundsException(); int numMoved = elementCount - toIndex; if (numMoved > 0) System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved); // Let gc do its work int newElementCount = elementCount - (toIndex-fromIndex); while (elementCount != newElementCount) elementData[--elementCount] = null; } /* public synchronized boolean addAll(int index, Collection<A> c) { modCount++; if (index < 0 || index > elementCount) throw new ArrayIndexOutOfBoundsException(index); int numNew = c.size(); ensureCapacityHelper(elementCount + numNew); int numMoved = elementCount - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); Iterator<A> e = c.iterator(); for (int i=0; i<numNew; i++) elementData[index++] = e.next(); elementCount += numNew; return numNew != 0; }*/ } |
public class Stack<A> extends Vector<A> { public A push(A item) { addElement(item); return item; } public synchronized A pop() { A obj; int len = size(); obj = peek(); removeElementAt(len - 1); return obj; } public synchronized A peek() { int len = size(); if (len == 0) throw new java.util.EmptyStackException(); return elementAt(len - 1); } public boolean empty() { return size() == 0; } public synchronized int search(A o) { int i = lastIndexOf(o); if (i >= 0) { return size() - i; } return -1; } } |
QUESTION construire une classe de Test "probante" quelles sont les erreurs détectées à la compil et les autres :
exemple :
public class TestV_Gene { public static void main(String args[]){ // Stack<A> pA=new Stack<Integer>(); erreur de compilation Stack<Integer> pA=new Stack<Integer>(); pA.push(new Integer(11)); pA.push(new Integer(22)); pA.push("coucou"); /* a la compilation : TestV_Gene.java:7: push(java.lang.Integer) in Stack<java.lang.Integer> cannot be applied to (java.lang.String) pA.push("coucou"); ^ */ pA.push(new Integer(33)); pA.push(new Integer(44)); pA.push(new Integer(66)); System.out.println("la pile de départ ! " + pA.toString()); } } |
à suivre ...
Comparable us Comparator :
Il y a 2 manières d'ordonner les objets : l'interface java.lang.Comparable fournit un natural order automatique pour les classses qui l'implante, Alors que l'interface java.util.Comparator donne au programmeur le contrôle complet de l'ordre des objets.