Bookmark and Share

Svårighetsgrad
Svårighetsgrad
Betyg (14 röster)
BetygBetygBetygBetygBetyg
 

Samlingsklasser

Inledning

Vi har tidigare diskuterat Array (Fält) i artikeln Fält. Denna artikel skall handla om lagringsstrukturer som dök upp första gången i C# 2.0. Med C# 2.0 infördes templates (något som funnits länge i t.ex. C++). Med templates (sv. mallar) gavs nu möjlighet att skriva generiska klasser på engelska kallat generic classes. En av de mest grundläggande behoven i ett programmeringsspråk är att ha bra lagringsstrukturer och det kan man få med generiska klasser. Vi väljer att benämna dessa generiska klasser inriktade mot lagring som samlingsklasser.

Vi skall alltså titta på de samlingsklasser som numera finns i C# (sedan 2.0) hämtade från namnutrymmet System.Collections.Generic. Dessa klasser kommer du att ha stor nytta av i många situationer.

Vilka är klasserna?

De klasser vi tar upp och ger exempel på är följande:

Det finns ytterligare en del klasser men vi har valt att begränsa oss till de som vi anser vara de mest använda.

List

Detta kan nog vara den mest använda lagringsklassen. Vi börjar med ett exempel där vi skapar en lista av int som vi dynamiskt utökar för att sedan göra en loop och skriva ut alla värden.

bild

Observera hur vi skapar vår List av int (rad 13). Tecknen < och > avslöjar att vi använder oss av en generisk klass. Inom < > anges den typ som klassen (i detta exempel Listan) skall använda. När vi arbetar med generiska lagringsstrukturer så anger typen vad strukturen skall kunna lagra. Efter skapandet går det ej att ändra typen annat än att skapa ett nytt objekt.

En List fungerar mer eller mindre som en bättre Array. I exemplet ovan visas några vanliga operationer som att lägga till och ta bort. Indexeringen fungerar även den som en Array med skillnaden att antalet element i listan ges av Count och inte av Length som det hade varit för en Array. Att skapa en List av en befintlig Array görs som på rad 21 i exemplet ovan. Du kan på så vis utnyttja listans fördelar, kanske manipulera innehållet för att senare konvertera listan tillbaka till Array med metoden ToArray() (ej med i exemplet ovan).

bild

Som visas i exemplet ovan så finns det metoder i för att beräkna summa, medelvärde, maxvärde och minimivärde (extension methods). Beroende på vilken typ du väljer att din lista skall lagra så kan dessa metoder skilja sig åt. Gör du t.ex. en lista av string så finns inte metoden Sum(). Vad en Extension method är tänkte vi inte ta upp i denna artikel men du bör kunna identifiera dem i Visual Studio (bild nedan) och lägga på minnet att dessa metoder kan du inte räkna med att alla typer har.

bild

Det finns en metod, Sort(), för att sortera listan som kan vara användbar.

Dictionary

En Dictionary har en funktion liknande ett lexikon. Varje element i en Dictionary består av två delar; en nyckel (key) och ett värde. Du blir tvungen att ange två typer när du skapar en Dictionary, en typ som skall användas som nyckel och en typ som skall användas som värde.

Vi börjar med ett exempel där string används som nyckel och int som värde:

bild

Tanken är att vi lagrar åldern hos varje person. Begreppet index får ett litet annan innebörd nu när andra typer än int kan användas som index. Kolla extra på rad 21. Vi indexerar med "Kalle" för att få ett värde.

En annan stor skillnad är hur en iteration genom samlingen går till. För att t.ex. lista allt innehåll måste vi använda klassen KeyValuePair i en foreach-loop. Vi behöver tala om hur värden och nycklar plockas ur samlingen.

När du jobbar med en Dictionary får du vara uppmärksammad på vissa saker. Anger du ett index fel, t.ex. om vi hade skrivit "kalle" på rad 21, så får vi ett Exception eftersom denna nyckel inte finns i samlingen (skillnad på stora och små bokstäver). Likaså får vi ett Exception om vi försöker lägga till en nyckel som redan finns i samlingen. Alltså gäller att

Nyckeln måste vara unik. Det kan inte finnas två "Kalle" i en Dictionary. Vi skall visa hur du håller koll på detta i nästa exempel.

I nästa exempel skall vi skapa ett histogram över simulerade tärningskast. Ett histogram mäter förekomsten av olika utfall/värden. Ett enkelt histogram över vädret skulle kunna vara hur många dagar på året som det har regnat. I exemplet nedan skall vi räkna hur många 1:or, 2:or, etc. som vi slår på en tärning. En Dictionary kan göra livet enklare:

bild

Eftersom vi slumpar så kommer du inte att få exakt samma resultat som bilden visar. Vi vet på förhand att vi bara kommer att slumpa tal mellan 1 och 6 så vi skulle kunna lägga till dessa nycklar innan vi går in i loop'en. Det är dock så pass vanligt att på förhand inte veta sådan information så vi gör en extra kontroll på rad 25 om vi behöver skapa en ny nyckel.

I resultatet ser vi också att nycklar listas i den ordning som de skapas, dvs. 1 kommer ej först. Detta skulle vi kunna lösa genom att göra en for-loop från 1 till 6 och istället för att använda foreach och KeyValuePair.

Det finns tyvärr ingen Sort() metod i Dictionary vilket leder oss till nästa klass...

SortedList

Denna samlingsklass har samma egenskaper som Dictionary med skillnaden att den är sorterad. Med SortedList anger du alltså nycklar och värden precis som Dictionary men SortedList förblir sorterad efter det att du lagt till element i samlingen.

Nu skall det nämnas att det finns en klass som heter SortedDictionary vilket hade varit det pedagogiska alternativet. Faktum är att SortedList och SortedDictionary fungerar nästan helt identiskt. Anledningen till att vi väljer SortedList är att denna har något bättre prestanda.

Istället för exempel på denna klass så erbjuder vi en övning

Övning 1

Skriv om Exempel 2 - Dictionary till att använda SortedList. Kör programmet några gånger så du upptäcker skillnaden.

Queue

En Queue (kö) fungerar precis som namnet antyder som en kö. Du kan köa element och sedan plocka ut dem i ordningen "först in", "först ut".

Metoden Enqueue() köar ett element (att jämföra med Add()). Metoden Dequeue() returnerar första elementet i kön (det äldsta elementet i kön) samtidigt som detta element plockas bort från kön.

Stack

Detta är liksom Queue en samlingsklass som fungerar som en kö, skillnaden är att en Stack fungerar som "först in", "sist ut". (Liknande LAS = Lagen om anställningsskydd). Metoderna heter något annorlunda; Push() för att lägga saker på stacken och Pop() för att plocka saker.

Sammanfattning

Gemensamt för generiska lagringsklasser är att de alla är mer dynamiska, dvs. de kan växa och minska efter behov på ett enkelt sätt. Jämför med en Array, vars storlek bestäms då den skapas, så är en Array alltså mer statisk.

Generiska klasser är också starkt typade. Med detta menas att en List bara kan lagra värden av typen int. Tidigare lagringsklasser (C# 1.1) som t.ex. ArrayList var inte starkt typad men hade de användbara dynamiska egenskaper som List.

Alla lagringsklasser som vi har tagit upp i denna artikel är specialiserade på ett eller annat vis. De kanske två mest använda förblir nog List och Dictionary men i vissa tillämpningar så underlättar det något enormt om man har en lagringsklass som är mer specifikt anpassad.

Övning 2

Skapa ett program där du tillåts att mata in tal tills dess att du matar in 0 då avslutas programmet. Efter varje inmatat tal skall du skriva ut medelvärdet av alla tal som matats in.

Övning 3

Använd en lista av strängar för att symbolisera en kortlek. T.ex. "k5" betyder klöver 5, "hKn" hjärter knekt, "sE" spader Ess, "rD" ruter dam, etc. Var effektiv när du fyller listan och använd loop'ar. Skriv sedan ett program som slumpvis drar kort från korleken tills dess att alla 52 kort är dragna. Skriv ut varje kort du "drar".

Övning 4

Bygg vidare på Övning 3 men använd en Dictionary istället. Som nyckel använder du "k5" etc eftersom dessa är unika i en kortlek på 52 kort. Som värde lagrar du int, Ess = 1, Kung = 13, etc. Dra nu två kort åt gången utan att "minska" kortleken och skriv ut "PAR" de gånger du drar två lika kort, dvs att värdet är lika. För att lösa detta måste du slumpvis kunna välja en nyckel i Dictionary vilket du kan genom att gå på Keys i Dictionary som i sig är en lista. Försök till sist beräkna hur många gånger det blir "PAR" om du gör 1000 dragningar.

Övning 5

Skriv ett program som räknar ut förekomsten av ord i en text(histogram). Indata kan lösas via konsollen eller genom att läsa en txt-fil. Resultatet presenteras sorterat med flest förekomster först. Var noga med att skala bort punkter, kommatecken, frågetecken och utropstecken samt tänk på/lös att "Det" inte är samma som "det".

Viktiga begrepp

  • templates
  • generiska klasser
  • generic classes
  • Extension method
  • key
  • starkt typade

Kommentarer

7 inlägg