Zie Scherp Scherper - Hoofdstuk 8
Honderden stoelen, allemaal hetzelfde, elk met een nummer.
Geen enkele stoel heeft een eigen naam nodig.
Na dit hoofdstuk kan je:
uitleggen waarom arrays veel data van hetzelfde type bundelen
arrays declareren, vullen en met een loop overlopen
begrijpen dat arrays by reference werken (en hoe je ze correct kopieert)
de System.Array-methoden gebruiken (Sort, Copy, IndexOf, …)
de klassieke loop-patronen zelf schrijven: optellen, tellen, de grootste zoeken
arrays als parameter en returntype in methoden gebruiken, ook met params
meerdimensionale en jagged arrays herkennen
Zeven losse variabelen voor zeven dagen regen?
Losse variabelen versus een array.
De index tussen [ ] mag een variabele zijn, dus genereer je ze in een loop:
Tip
Vanaf 3 of meer variabelen van dezelfde soort is een array bijna altijd het antwoord.
Sorry dat we weer in het diepe duiken. Maar kijk eens achterom: schrik je hé? Je hebt al een aardige weg afgelegd sinds ik je voor het eerst in het zwembad gooide.
Alles wordt kinderspel als je er maar lang genoeg mee bezig bent.
Belangrijk
Eens de lengte vastligt, kan een array niet meer groeien of krimpen. (Lists in hoofdstuk 12 lossen dat op.)
Tip
Sinds C# 12 mag je in de plaats van de accolades ook vierkante haken schrijven: string[] kleuren = ["red", "green", "blue"];. Die vorm mag ook ná de declaratielijn, de accolades niet.
new int[3] maakt drie vakjes die meteen de defaultwaarde van hun type bevatten:
0 int, double
false bool
\0 char
null string
Waarschuwing
new string[5] geeft je geen vijf lege teksten, maar vijf keer null. myColors[0] == "" is dus false, en myColors[0].Length crasht. Vul een string-array eerst zelf in.
List?Array
Ligt het aantal elementen op voorhand vast (12 maanden, 64 vakjes op een schaakbord), dan neem je een array
List
Kan er tijdens de uitvoer iets bij komen of wegvallen (een winkelmandje, spelers die zich aanmelden), dan heb je een List nodig (hoofdstuk 12)
Waarschuwing
Een array heeft Length, een List heeft Count. Een array heeft geen Count, en de foutmelding die je dan krijgt zegt niet bepaald duidelijk wat er scheelt.
Tip
Array.Resize(ref cijfers, 5) lijkt een uitweg, maar maakt achter de schermen een nieuwe array en kopieert alles over. Bij grote arrays neem je beter meteen een List.
Benader een element met array[index]
De index start bij 0
een array met lengte 5 heeft indices 0 tot en met 4
Een Vlaamse ezelsbrug voor 0-gebaseerde indexering:
6 Een array met lengte 6 heeft 6 elementen (6 pintjes op de toog)
0 Het eerste element zit op index 0: eentje is geentje
5 Het laatste zit dus op index 5, niet 6
Waarschuwing
array[array.Length] valt er altijd net naast: dat is precies de IndexOutOfRangeException.
De eerste stoel heeft nummer 0.
Zes stoelen: 0 tot en met 5.
array.Length geeft het aantal elementen. Ideaal als grens van een loop:
Waarschuwing
Console.WriteLine(myColors) toont niet de inhoud. Overloop de array element per element.
Een IndexOutOfRangeException. We bouwen aan een gebouw met 3 verdiepingen; vraag ik mijn metser om op de zesde te metsen, dan valt hij eraf.
Belangrijk
De compiler vangt dit niet: je ontdekt het pas bij de uitvoer. Hackers misbruiken net dit soort fouten in buffer overflow attacks.
Het ticket zegt stoel 6. De rij stopt bij 5.
Dat is een IndexOutOfRangeException.
Value types
(int, double, …) bevatten de waarde zelf
Reference types
bewaren een adres naar de data elders in het geheugen
Belangrijk
Arrays werken by reference. Een array-variabele bevat dus niet de array zelf, maar een verwijzing ernaar.
Kopieer je het zaalplan, dan heb je nog altijd maar één zaal.
Legt de ene een jas op een stoel, dan ziet de andere die jas ook.
= maakt een alias. Kopieer dus element per element, in een array die even groot is:
Tip
Of gebruik Array.Copy(bron, doel, aantal) uit de System.Array-bibliotheek.
Je roept deze methoden op Array op en geeft jouw array als argument mee:
myColors
red green yellow orange blue
Array.Sort
blue green orange red yellow
Array.Reverse
yellow red orange green blue
Ze geven niets terug: ze krijgen de referentie naar jouw array en herschikken die ter plekke. Na die ene regel is myColors zelf veranderd.
Tip
Van groot naar klein sorteren doe je met een Sort() gevolgd door een Reverse().
Allebei werken ze met een startindex en een aantal elementen:
Opmerking
Clear verandert enkel de inhoud, niet de lengte. Bij Copy moet de doel-array al bestaan, en krijg je wél twee aparte arrays in het geheugen.
Array.IndexOf laat je array met rust en geeft een getal terug:
Die -1 is de afspraak voor “niet gevonden”: een index kan nooit negatief zijn.
Intern overloopt de methode je array element per element, precies zoals je het zelf zou schrijven.
Belangrijk
Test op -1 vóór je het resultaat als index gebruikt, anders vlieg je met myColors[-1] tegen een IndexOutOfRangeException aan.
Staat je array gesorteerd, dan hoef je niet alles te overlopen:
BinarySearch kijkt in het midden en weet na die ene vergelijking al in welke helft het element moet liggen.
Belangrijk
Op een ongesorteerde array krijg je geen foutmelding, wel af en toe een fout antwoord. Gebruik dan IndexOf of een eigen loop.
Zodra je iets wil optellen of tellen helpt geen enkele kant-en-klare methode je nog. Je resultaat-variabele maak je aan vóór de loop:
Waarschuwing
De cast naar double is nodig: som en Length zijn allebei int, dus zonder cast doe je een gehele deling. Wil je tellen in plaats van optellen, dan staat er enkel een if in de loop.
Hiervoor bestaat geen kant-en-klare methode. Je houdt de beste kandidaat tot nu toe bij, en meteen ook zijn index:
Belangrijk
Start met punten[0], niet met 0. Met int hoogste = 0; krijg je een fout antwoord zodra alle waarden negatief zijn. Daarom start de loop ook bij index 1.
Staat rugnummer 12 in de top 5? Je loop heeft twee redenen om te stoppen: het einde van de array, of een geslaagde zoektocht.
Waarschuwing
index++ staat in de else. Verhoog je de teller altijd, dan wijst index achteraf één positie te ver. Met een for-loop en een break mag ook: je bewaart de index dan in een variabele die op -1 start.
Naam en prijs staan op dezelfde index in twee aparte arrays:
Links staan naam en prijs op dezelfde index. Rechts verhuisden de namen wel, de prijzen niet.
Waarschuwing
Array.Sort(producten) breekt de link: de namen verhuizen, de prijzen blijven staan. In hoofdstuk 9 bewaart één object de naam én de prijs samen.
Het hoofdstuk over klassen, in drie lijstjes:
map in het boek
8_klassen
hoofdstuk
H9
map met oefeningen
9_klassen
Map 17 en map 19 bestaan niet meer
20_exceptions hoort bij H10 én H14
Lezen mag, met vierkante haken en Length, precies zoals bij een array:
Belangrijk
Ze geven allemaal een nieuwe string terug en laten het origineel ongemoeid. boek.Trim(); op zichzelf doet dus niets: je gooit het resultaat weg. Schrijf boek = boek.Trim();. Merk het verschil met Array.Sort, die jouw array wél ter plekke aanpast.
Split levert een array op, Join voegt er terug één string van. Hoeveel elementen Split oplevert weet je op voorhand niet, dus gebruik ook hier delen.Length in je loop.
Tip
Console.WriteLine(string.Join(", ", myColors)); is meteen de kortste manier om een hele array op één lijn te tonen.
Tip
Wil je dat een methode je origineel niet aanraakt, geef dan een kopie mee die je zelf element per element hebt gevuld.
In de signatuur geef je geen grootte mee; gebruik .Length in de methode:
Waarschuwing
static void LeesData(int[6] arr) is fout: een grootte in de parameterlijst mag niet.
Nu snap je de params object[] arg in de signatuur van Console.WriteLine. Zet je params voor een array-parameter, dan steekt C# de losse waarden zelf in een array:
Waarschuwing
Er mag maar één params-parameter zijn, en die staat achteraan: static void Log(string titel, params string[] regels).
Steven wil de scores herstellen naar een verse, lege array. De A.I. gaf hem dit:
“Reset zet er een nieuwe, lege array in de plaats, dus dit toont 0”, zegt hij.
Opmerking
Er verschijnt gewoon 10. Wat je meegeeft is een kopie van de wegwijzer: die kopie mag je in de methode naar iets anders laten wijzen, de variabele in Main blijft naar de originele array kijken. Een element aanpassen (scores[0] = 99) werkt wél door, de hele array vervangen niet.
Nu snap je string[] args in Main: het zijn de opstartparameters.
Belangrijk
Test eerst args.Length >= 3, dan pas args[2]. De && stopt links bij false en zo vermijd je een crash.
Met een komma in de haken maak je een rechthoekig rooster:
Eerst de rij, dan de kolom.
beide starten bij 0
Tip
.Length geeft het totaal (hier 28); GetLength(0) en GetLength(1) de afzonderlijke dimensies, .Rank het aantal dimensies.
Van bovenaf is de zaal een rooster.
“Rij 3, stoel 7” wordt zaal[3, 7].
Twee geneste loops: de buitenste over de rijen, de binnenste over de kolommen van die rij.
Ook een rooster gaat by reference naar een methode; in de parameter herhaal je de komma: static void ToonRooster(int[,] rooster).
Waarschuwing
Gebruik GetLength(...) en geen harde getallen. En verwissel de indices niet: regen[dag, week] compileert perfect, maar crasht bij de uitvoer.
Een 2D-array werkt zolang ieder vakje hetzelfde betekent.
een spelbord, een doolhof, pixels, regen per dag
Hier betekent elke kolom iets anders: alles moet string zijn, en boeken[1, 1] zegt nergens dat kolom 1 de auteur is.
Tip
Vanaf hoofdstuk 9 wordt dit een klasse Boek, in hoofdstuk 12 bewaard in een gewone 1D-array. De tweede dimensie verdwijnt.
Een array van arrays met verschillende lengtes. Let op de dubbele [][]:
Opmerking
Je moet ze vooral kunnen herkennen. Zelf schrijf je liever een rechthoekige [,]-array, of een collectie uit hoofdstuk 12: iedere rij apart aanmaken en apart op zijn lengte controleren is omslachtig en foutgevoelig.
Vooraan passen minder stoelen dan achteraan.
Elke rij is een eigen array, met een eigen Length.
Je hebt zelden meer dan 2 of 3 dimensies echt nodig
Heb je meer? Herdenk je probleem. Vaak lukt hetzelfde met twee 2D-arrays in plaats van een 4D-array
Hoe meer dimensies, hoe moeilijker voor te stellen en te debuggen
Tip
En vanaf hoofdstuk 12 maken collecties veel van dit werk een stuk aangenamer.
Een array bundelt veel waarden van hetzelfde type onder één naam; index start bij 0
Overloop arrays met een loop en .Length; let op de IndexOutOfRangeException
Arrays werken by reference: = maakt een alias, kopieer dus element per element
System.Array en de string-methoden besparen werk; optellen, tellen en de grootste zoeken schrijf je zelf
Arrays gaan by reference naar methoden (params voor een los aantal argumenten); een 2D-array dient voor een rooster, jagged moet je enkel herkennen
Tip
Volgende halte: object-georiënteerd programmeren, met klassen en objecten.
Kennisclips
Hoe doen andere programmeertalen hetzelfde? En een korte zoek-de-fout, om zo kritisch mogelijk naar code te leren kijken.
Onderstaande C#-code wil alle elementen van de array tonen, maar crasht met een IndexOutOfRangeException. Wat loopt er mis?
Opmerking
De testconditie gebruikt <= in plaats van <. Bij lengte 3 loopt i zo tot en met 3, maar de geldige indices zijn enkel 0, 1 en 2. Bij getallen[3] ga je buiten de array en krijg je de exception. Vervang i <= getallen.Length door i < getallen.Length.
In Python bestaat geen array met vaste grootte, wel een dynamische list:
Totaal anders dan een C#-array (vaste lengte, 1 type). Lijkt eerder op de List uit hoofdstuk 12.
C# gooit netjes een IndexOutOfRangeException. In C is er geen enkele grenscontrole:
Je leest dan zomaar willekeurige geheugeninhoud of je programma crasht. Net dat maakt buffer overflow attacks mogelijk.
Ook JavaScript-arrays werken by reference, dus = maakt een alias, geen kopie:
Heel gelijkaardig aan C#. Een echte kopie: let kopie = [...ploegen];.
Een handige samenvattende poster (Opgelet: gemaakt m.b.v. ChatGPT Image Gen 2).