F 4 En abstrakt idé ordnad sekvens Läsanvisning: kap 2.1-2.3 Idag handlar det främst om listor implementerade med fält jmf Javas ArrayList ett interfacet för sekvenser (List) implementation av sekvenser! med fält! med länkad struktur ArrayList som underliggande struktur iteratorer och så skall vi beräkna komplexiteten för selectionsort eller/och lösa en rekursionsekvation under sista passet. Näst-nästa gång (dvs F5 på fredag): Handlar det om listor implementerade med länkade strukturer. - dubbellänkade listor - listor och rekursion An ordered collection (also known as a sequence). The user of this interface has precise control over where in the list each element is inserted. The user can access elements by their integer index (position in the list), and search for elements in the list. Unlike sets, sequences typically allow duplicate elements. Jmf Javas List -interface. -------------------------- Not1: Index från 0 dvs 0 <= index < size(). Not2: ordered collection betyder inte att den är sorterad. Not 3: komma åt element via index innebär inte att det är billigt. Beroende på den underliggande representationen kan man ha direktaccess till alla element (fält) eller behöva starta från början (länkad struktur). F4 Listor implementerade med fält 1 F4 Listor implementerade med fält 2 Övergripande designval När vi skall designa en sekvens (eller annan adt) ställs vi inför många designbeslut. Hur skall gränssnittet se ut?! Skall det tex uppfylla kanonisk form för publika klasser, vilka primitiver behövs, Vilken underliggande representation skall vi välja?! Ett fält eller länkad struktur?! Enkellänkad eller dubbellänkad lista eller annan variant?! Skall vi ha extranod? Informationsnod? I så fall vilken information? Designval på mer detaljerad nivå Hur gör vi med detaljerna?! Skall den vara generisk?! Skall den vara självexpanderande?! Skall vi ha en speciell klass för listnoder?! Skall listan kunna gå igenom sig själv dvs skall det finnas iterator?! osv För alla gäller att dom också har en hur del dvs hur gör vi den självexpanderande osv.! Hur mycket större skall den bli varje gång?! Hur gör man en iterator?! Hur löser man genomgång och samtidig uppdatering?! Om kanonisk form:! hur gör man clone, equals, hashcode, serializable? Vi kommer att implementera en enkel lista tagen från boken samt parallellt titta på state of the art lösningar på problemen ovan genom att titta på Java's lösningar. F4 Listor implementerade med fält 3 F4 Listor implementerade med fält 4
Gränssnitt - valet är viktigt och svårt. Att designa ett generellt gränssnitt (specifikation) som alltid fungerar är svårt. Det är vanligen inte förrän man gjort ett antal gränssnitt (för tex en lista) som man inser vilka operationer som är generellt användbara Vi kan utgå från (en del av) Javas List interface. boolean add(e o) Appends the specified element to the end of this list. void add(int index, E element) Inserts the specified element at the specified position boolean contains(object elem) Returns true if this list contains the specified element. E get(int index) Returns the element at the specified position in this list. int indexof(object elem) Searches for the first occurence of the given argument, testing for equality using the equals method. boolean isempty() Tests if this list has no elements. E remove(int index) Removes the element at the specified position in this list. E set(int index, E element) Replaces the element at the specified position in this list with the specified element. int size() Returns the number of elements in this list. Representationer F4 Listor implementerade med fält 5 F4 Listor implementerade med fält 6 Representation med fält Tex: int[] arr = new int[capacity] index innehåll 0 33 size 1 88 listan 2 55 capacity 3 tomt Pros/Cons: + Lätt att lägga till på slutet + Direktaccess till element nr i - Tillägg/borttag i mitten är dyrt/besvärligt - Maximerad storlek --> detta kan vi fixa - Kontinuerligt minne insättning på plats (add) O(n), insättning sist (add) O(1), remove, indexof O(n), previous, get, set O(1) (n=size) (pros/cons is an abbreviation of the latin phrase pro et contra = for and against ) F4 Listor implementerade med fält 7 KWArrayList public class KWArrayList<E> { /** The default initial capacity private static final int INITIAL_CAPACITY = 10; /** The underlying data array private E[] thedata; /* private transient E[] elementdata; J6 transient Object[] elementdata; J8 /** The current size private int size = 0; /** The current capacity private int capacity = 0; /** Construct an empty KWArrayList * with the default initial capacity public KWArrayList() { capacity = INITIAL_CAPACITY; thedata = (E[])new Object[capacity];. F4 Listor implementerade med fält 8
Transient Ej serialiserbar = ej möjlig att streama automatiskt Två anledningar för transient: - Objektet är inte serialiserbart då kommer en exception att kastas av systemet om den inte är transistent Serialisering sker normalt automatiskt med metoder från ObjectOutputStream, ObjectInputStream - Objektet är redundant eller bör ej tex den kan beräknas från andra variabler, eller tillståndsinformation den kanske innehåller säkerhetsinfo, E[] elementdata = (E[]) new Object[size]; String[] elementdata = Borde ge ClassCastException: Object[] is not a String[] (String[]) new Object[size]; Man får Note: DirectedGraph.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. Men man kan alltid fuska :-) @SuppressWarnings("unchecked") I fallet med ArrayList så sköter klassen själv serialiseringen genom att överskugga writeobject() and readobject() från ObjectOutputStream och ObjectInputStream se interfacet Serializable. F4 Listor implementerade med fält 9 F4 Listor implementerade med fält 10 serialversionuid Vi kan lika gärna ta ett problem till med serialisering när vi ändå håller på: Det är viktigt att den som läser in serialiserade objekt har en likadan miljö som den som skrev objekten. private static final long serialversionuid = 8683452581122892189L; serialversionuid är ett serienummer som används vid de-serialisation för att verifiera att sändaren och mottagaren av de serialiserade objekten har samma miljö dvs har laddat samma klasser för dessa objekt. Man bör skapa den själv, annars skapas en av systemet men den är kompilatorberoende. Jmf med checksumma - siffror som bifogas för att man ska kunna märka ändringar och fel. (Men checksummor räknas vanligen fram) F4 Listor implementerade med fält 11 add(last) och reallocate /** Add an entry to the data, inserting it last. @param anentry - value to be inserted public boolean add(e anentry) { if (size == capacity) { reallocate(); thedata[size] = anentry; size++; return true; // always /** Allocate a new array private void reallocate() { capacity = 2 * capacity; E[] newdata = (E[])new Object[capacity]; for(int i = 0; i < size; i++){ newdata[i] = thedata[i]; thedata = newdata; // System.arraycopy(theData, 0, // newdata, 0, size); F4 Listor implementerade med fält 12
Java 6: public boolean add(e o) { ensurecapacity(size + 1); elementdata[size++] = o; return true; public void ensurecapacity( int mincapacity) { modcount++; int oldcapacity = elementdata.length; if (mincapacity > oldcapacity) { Object olddata[] = elementdata; int newcapacity = (oldcapacity*3)/2+1; if (newcapacity < mincapacity) newcapacity = mincapacity; elementdata = (E[])new Object[newCapacity]; System.arraycopy(oldData, 0, elementdata, 0, size); Java 8: int newcapacity = oldcapacity + (oldcapacity >> 1); remove (från KWArrayList) /**Remove an entry based on its index. @param index - The index of the entry to be removed @return The value removed @throws ArrayIndexOutOfBoundsException - if the index is negative or if it is greater than or equal to the current size public E remove(int index) { if (index < 0 index >= size) { ArrayIndexOutOfBoundsException (remove: "Index= "+index+", Size= "+size)); E returnvalue = thedata[index]; for(int i = index+1; i < size; i++){ thedata[i-1] = thedata[i]; size--; return returnvalue; J8: elementdata[--size] = null; // clear to let gc do its work F4 Listor implementerade med fält 13 F4 Listor implementerade med fält 14 add at index (from ArrayList) /**Inserts the specified element at the specified position in this list. Shifts the element currently at that position (if any) and any subsequent elements to the right (adds one to their indices). @param index index at which the specified element is to be inserted. @param element element to be inserted. @throws IndexOutOfBoundsException if index is out of range i.e. if (index < 0 index > size()) public void add(int index, E element){ if ( index < 0 index > size ) IndexOutOfBoundsException( "add: Index= "+index+", Size= "+size); if(size == capacity) {reallocate(); System.arraycopy(theData, index, thedata, index+1,size-index); thedata[index] = element; size++; F4 Listor implementerade med fält 15 Från Javas API om ArrayList Resizable-array implementation of the List interface. Implements all optional list operations, and permits all elements, including null. In addition to implementing the List interface, this class provides methods to manipulate the size of the array that is used internally to store the list. (This class is roughly equivalent to Vector, except that it is unsynchronized.) Prestanda - ArrayList The size, isempty, get, set, iterator, and listiterator operations run in constant time. The add (last) operation runs in amortized constant time, that is, adding n elements requires O(n) time. All of the other operations run in linear time (roughly speaking). The constant factor is low compared to that for the LinkedList implementation. ( så add (index, e) O(n) ) F4 Listor implementerade med fält 16
behandla i tur och ordning -tostring Att skriva ut, eller på annat sätt behandla i tur och ordning, elementen i ett fält (eller annan samling) inifrån samlingen (KWArrayList) kan göras med nåt i stil med: p är här en iterator över fältet public String tostring() { StringBuilder buf = new StringBuilder(); buf.append("[ "); for (int p=0; p < size-1; p++) { buf.append(string.valueof ( thedata[p]) ); buf.append(" -> "); // eller, if (size>0) buf.append(string.valueof ( thedata[size-1]) ); buf.append(" ]"); return buf.tostring(); public static String valueof(object obj) { return (obj == null)? "null" : obj.tostring(); public static String valueof(int i) { return Integer.toString(i); public static String valueof(boolean b) { return b? "true" : "false"; Men hur gör vi om vi vill göra det som användare av samlingen dvs utanför klassen? F4 Listor implementerade med fält 17 F4 Listor implementerade med fält 18 iteratorer Vi vill att samlingen själv håller reda på vilket som är nästa element dvs att den inkapslar en aktuell position i fältet, i förra fallet indexet p. Sammanhang: Ett objekt (samlingen) innehåller andra objekt (elementen). Klienter vill ha tillgång till elementen Koden skall vara så oberoende som möjligt av typen på elementen. Samlingen skall inte avslöja den interna strukturen. Iteratorn skall naturligtvis skapas i den klass den itererar över så den får vara en inre klass dvs en klass inuti vår KWArrayList klass. Det kan finnas flera samtidiga klienter dvs iteratorn skall klara förändringar Lösning: ett iteratormönster Vi definierar först ett interface "Iterator" public interface Iterator<E> { // Returns true if the iteration // has more elements. public boolean hasnext(); // Returns the next element in the // iteration. public E next(); // Removes from the underlying // collection the last element // returned by the iterator // (optional operation). public void remove(); Varje samling har sedan en factory -metod "iterator" som returnerar en klass som implementerar gränssnittets protokoll. Varje sådan klass måste hålla reda på sin aktuella position i samlingen. public Iterator<E> iterator() { return new ArrayListIterator(); F4 Listor implementerade med fält 19 F4 Listor implementerade med fält 20
public class KWArrayList<E> { private E[] thedata; private int size = 0; *** all annan kod utesluten *** public Iterator<E> iterator() { return new ArrayListIterator(); // privat inre klass private class ArrayListIterator implements Iterator<E> { /** index of the current item. private int currentindex = 0; public boolean hasnext( ) { return (currentindex >= 0 && currentindex < size); public E next( ) { if (!hasnext() ) { // or return null; NoSuchElementException(); else { return thedata[currentindex++]; // end ArrayListIterator // end KWArrayList Remove Även om vi inte implementerar remove måste vi ha med: public void remove( ) { UnsupportedOperationException(); remove är lite speciell. Så här står det i iterator interfacet för Java Removes from the underlying collection the last element returned by the iterator (optional operation). This method can be called only once per call to next. The behavior of an iterator is unspecified if the underlying collection is modified while the iteration is in progress in any way other than by calling this method. Throws: UnsupportedOperationException - if the remove operation is not supported by this Iterator. IllegalStateException - if the next method has not yet been called, or the remove method has already been called after the last call to the next method. Skall man ta hänsyn till att samlingen kan uppdateras under genomgången (och att den då bör avbrytas) så blir det lite mer komplicerat. F4 Listor implementerade med fält 21 F4 Listor implementerade med fält 22 Använda iteratorn i ett program KWArrayList<Integer> thelist = new KWArrayList<Integer>(); // fyll med tal for(int i = 0; i < 8; i++ ) { thelist.add( i, i ); Iterator<Integer> it = thelist.iterator(); while ( it.hasnext() ) { Integer p = it.next(); System.out.print( String.valueOf(p) +" "); // eller gör nåt annat Både Container klassen (i det här fallet KWArrayList) och anroparen måste göra import java.util.*; för att komma åt iterator interfacet iteratorer - ändra innehållet i listan Man kan även lägga till andra metoder i iteratorn, tex en set metod om vi vill kunna ändra innehållet samtidigt som vi går igenom listan. Men vi måste lösa ett problem: Det stod ju i remove metoden att: The behavior of an iterator is unspecified if the underlying collection is modified while the iteration is in progress in any way other than by calling this method. Hur kan man hantera det? I KWArrayList deklarerar vi en variabel int modcount = 0; som sedan ökas med ett så fort en ändring görs i listan tex av add, set eller någon annan metod. public void add(int index, E element){ if ( index < 0 index > size ) kod utesluten thedata[index] = element; size++; modcount++; F4 Listor implementerade med fält 23 F4 Listor implementerade med fält 24
Nu kan iteratorn spara modcounts värde och jämföra med det innan ett värde returneras /** * The modcount value that the iterator * believes that the backing List * should have. If this expectation is * violated, the iterator has * detected concurrent modification. int expectedmodcount = modcount; Vi håller också reda på vilket element som senast returnerats av iteratorn /** * Index of element returned by most * recent call to next. Reset to -1 if * this element is deleted by a call to * remove. private int lastreturned = -1; nu kan vi kolla om listan ändrats genom final void checkforcomodification() { if (modcount!= expectedmodcount) F4 Listor implementerade med fält 25 Men hjälp av modcount och lastreturned kan vi nu skriva en set metod (i iteratorn) som kan ändra i listan samtidigt som vi går igenom den: /** Replace the last item returned * with a new value. * @param item The new value * @throws ConcurrentModificationException * if next was not called prior to * calling this method public void set(e item) { if (lastreturned == -1) { checkforcomodification(); try { // make a call to // the outer class set method KWArrayList.this.set (lastreturned, item); expectedmodcount = modcount; catch(indexoutofboundsexception e) { F4 Listor implementerade med fält 26 remove i iteratorn (lastreturned förkortad till lastret) public void remove() { if ( lastret < 0) IllegalStateException(); checkforcomodification(); try { KWArrayList.this.remove(lastRet); currentindex = lastret; lastret = -1; expectedmodcount = modcount; catch (IndexOutOfBoundsException ex){ ListIterator I Collections finns 2 iterator interface Iterator som stödjer genomgång i en riktning (framåt) och ListIterator (som ärver Iterator) som stödjer genomgång både bak och framlänges. hasprevious(), previous(), Alla konkreta samlingar i Java implementerar Iterator och många implementerar också ListIterator. Om samlingen ändras under genomgången så är alltså Strategin i Collections API att då skall iteratorns metoder generera en exception: ConcurrentModificationException Iteratorns egen remove/set kan dock användas utan att detta sker (den tar ju bara bort/ändrar element som redan har passerats). Remove är listad som "optional". F4 Listor implementerade med fält 27 F4 Listor implementerade med fält 28