I denna labb får ni implementera en första ADT, mängd, en första datastruktur, dynamisk array, och en klassisk algoritm, binärsökning (binary search). Ni får repetera java-koncepten gränssnitt och klass och i del B också generics och standardinterfacet Comparable
. Ni får mäta exekveringstid empiriskt och analysera programkods tidskomplexitet. Labben består av två delar, A och B.
Ett enkelt sätt att implementera den abstrakta datatypen mängd är genom att lagra elementen i en array. Datastrukturen ska kunna lägga till element, ta bort element och avgöra om ett heltal är medlem i mängden.
Datastrukturen är tänkt att användas i tillämpningar där det sker relativt få förändringar av mängden men många förfrågningar om ett tal ingår i mängden. Implementationen ska alltså vara snabb på att avgöra om ett tal är medlem i en mängd, men behöver inte vara så snabb på att lägga till och ta bort element. Ett sätt att åstadkomma detta är att använda en array, lagra elementen sorterat och leta efter element med binärsökning. Detta ska ni göra.
I del A av labben ska ni implementera en datastruktur för en mängd av heltal. Implementera följande gränssnitt (filen IntSet.java
):
public interface IntSet {
public void add(int element);
public boolean contains(int element);
public void remove(int element);
}
Interfacet har tre metoder som motsvarar ungefär de metoder med samma namn i standardgränssnittet Set
.
add
lägger till heltalet element
till mängden om det inte redan finns där. Finns det redan så förblir mängden oförändrad.contains
returnernar true
om heltalet element
finns i mängden och false
annars. Mängden förblir oförändrad.remove
tar bort heltalet element
från mängden om det finns där. Finns det inte i mängden så förblir den oförändrad.Den klassen som implementerar gränssnittet ska heta BinSearchIntSet
och ha en konstruktor utan argument (BinSearchIntSet()
) som skapar en tom mängd. Elementen ska lagras i en primitiv array (typen ska vara int[]
).
Ni ska implementera en dynamisk array, d.v.s. varje gång den är full ska kapaciteten (arrayens storlek) ökas multiplikativt (multipliceras med konstant större än 1, t.ex. 2). Den initiala kapaciteten kan sättas till något litet tal, t.ex. 1
. Tänk på att skilja på den logiska storleken (mängdens storlek) och arrayens faktiska storlek (kapaciteten). Arrayens kapacitet behöver aldrig minskas (men får minskas om ni vill) vid borttagning av element.
Elementen ska lagras sorterade (i växande eller sjunkande ordning) i arrayen. Metoden contains
ska använda binärsökning för att lokalisera den plats i array där det eftersökta heltalet måste finnas om det är medlem i mängden.
Om ni vill kan även add
och remove
lokalisera rätt plats med hjälp av binärsökning. Implementera i så fall en hjälpmetod som utför binärsökning och anropas av alla de tre metoderna. Att använda binärsökning för add
och remove
är valfritt för det påverkar inte väsentligt exekveringstiden för dem.
Klasser och metoder från standardpaket (förutom java.lang
) eller andra källor får inte användas.
Testa er implementation genom att skriva en körbar klass där en instans skapas och några operationer utförs och kontrollera sedan att resultaten av anropen till contains
är de förväntade.
Exempelvis:
class ManTest {
public static void main(String[] args) {
IntSet set = new BinSearchIntSet();
set.add(1);
set.add(2);
set.add(1);
set.remove(3);
set.remove(1);
System.out.println(set.contains(1));
System.out.println(set.contains(2));
System.out.println(set.contains(3));
}
}
Denna kod bör skriva ut
false
true
false
Testa några olika sekvenser av operationer.
Filen Test.java
är ett program som automatiskt testar implementeringar av gränssnittet IntSet
. För att testa er klass BinSearchIntSet
kör ni programmet så här:
java Test BinSearchIntSet
Programmet kör tester i fem sekunder. Om en bugg hittas så visas den sekvens av operationer som exponerade buggen. Denna sekvens kan ni kopiera och köra som ett manuellt test. Det gör det enklare att avlusa programmet och komma underfund om vad som är fel.
Innan ni skickar in labben ska er BinSearchIntSet
klara testprogrammet, d.v.s. det ska köras i 5 sekunder, avsluta och säga att inga fel hittats.
Filen Benchmark.java
är ett program som mäter den genomsnittsliga exekveringstiden för metoden contains
i en implementering av IntSet
. Den gör det för olika storlekar på mängden. För att mäta exekveringstiden för contains
i er klass BinSearchIntSet
kör ni programmet så här:
java Benchmark BinSearchIntSet
Att körningen avslutar med timeout leder i sig inte till att ni underkänns. Det är okej så länge ni får tillräckligt många tider för att kunna analysera utvecklingen (4-5 stycken).
Den givna filen LinSearchIntSet.java
är en implementering av IntSet
där linjärsökning används i contains
. Ni kan beräkna exekveringstiderna för denna implementering genom att skriva
java Benchmark LinSearchIntSet
Begrunda hur förändringen av exekveringstiderna skiljer sig åt för de två implementeringarna då storleken på mängden blir stor.
Skriv en textfil med namnet komplexitet.txt
som innehåller följande:
contains
i er klass BinSearchIntSet
uttryckt som en funktion av, n, antal element i mängden.contains
. Denna ska förstås också resultera i den tidskomplexitet ni angivit.Nu ska ni implementera en generisk datastruktur för mängder, d.v.s. en datastruktur där elementtypen inte är fixerad till heltal. Skriv en klass med namnet BinSearchGenSet
som implementerar följande gränssnitt (filen GenSet.java
).
public interface GenSet<E> {
public void add(E element);
public boolean contains(E element);
public void remove(E element);
}
Liksom i del A ska klassen ha en konstruktor utan argument som skapar en tom mängd.
För att kunna lagra elementen sorterat behöver det finnas en ordning definierad för elementtypen, E
. Kräv detta genom att specificera att E
ska implementera standardgränssnittet Comparable
. Detta kan man göra genom att ge klassen följande deklaration:
public class BinSearchGenSet<E extends Comparable<? super E>> implements GenSet<E>
Detta innebär att metoden compareTo
kan användas i koden i klassen för att jämföra objekt av typen E
. (Den komplicerade deklarationen <E extends Comparable<? super E>>
förklaras i kursboken i sektion 1.5.5 andra upplagan och sektion 1.5.6 in den tredje.)
I denna implementation ska inte en primitiv array användas. De fungerar inte så bra tillsammans med generics. Använd istället standardklassen ArrayList
. I del B slipper ni alltså hantera växande av arrayen samt insättning och borttagning på angivet index.
Metoden contains
ska återigen använda binärsökning. Kopiera och anpassa koden ni skrev i del A för det generiska fallet.
Med undantag av ArrayList
får klasser och metoder från standardpaket (förutom java.lang
) eller andra källor inte användas.
Testa er implementation manuellt på samma sätt som i del A. Ni kan t.ex. testa den med strängar som element, exempelvis börja med dessa programrader:
GenSet<String> kurser = new BinSearchGenSet<>();
kurser.add("DAT037");
System.out.println(kurser.contains("DAT037"));
Filen BinSearchGenSetAsIntSet.java
är en wrapper-klass som omvandlar er klass BinSearchGenSet
till en IntSet
. Använd denna för att köra det automatiska testet på er BinSearchGenSet
. Skriv så här:
java Test BinSearchGenSetAsIntSet
Innan ni skickar in labben ska er BinSearchGenSet
klara testprogrammet.
Jämför exekveringstiden för den generiska och ickegeneriska varianten av binärsökning. Skriv
java Benchmark BinSearchGenSetAsIntSet
och jämför med värdena för BinSearchIntSet
. Tiderna bör bara skilja sig lite åt. Om inte så är något fel. Med "lite" menas i detta fall att tiderna inte skiljer sig mer än några gånger. Att BinSearchGenSet
är dubbelt så långsam som BinSearchIntSet
är helt okej. Ni behöver inte rapportera resultatet av jämförelsen, men måste se till att exekveringstiden för den generiska varianten är rimlig.
Se till att ni noga följer de allmänna reglerna och riktlinjerna för laborationerna.
Innan ni skickar in ska ni se till att er kod klarar de automatiska tester som beskrivs ovan. När ni lämnar in kompileras och testas er kod automatiskt. Den testas med samma automatiska testprogram som ni har tillgång till.
Skicka in följande filer:
BinSearchIntSet.java
komplexitet.txt
BinSearchGenSet.java