C# loendite ja üldkogumite õppimise juhend
See juhend annab põhjaliku ülevaate List klassist, geneeriliste elementide toimimisest ja andmetega manipuleerimisest .NET-keskkonnas. See sünteesib teavet kollektsioonide sisemise struktuuri, nende jõudluse ja LINQ kasutamise parimate tavade kohta.
——————————————————————————–
1. Loendi ülevaade ja peamised omadused
List on tugevalt tüübitud objektide kogum, millele pääseb ligi indeksi abil. See esindab dünaamilist massiivi ja on üldistatud vaste ArrayListile.
Peamised omadused:
- Nimeruum: Leidub failis System.Collections.Generic.
- Dünaamiline suurus: Erinevalt standardsetest massiividest võib List suurus elementide lisamisel või eemaldamisel kasvada või kahaneda.
- Tüpiseerimine: Pakub kompileerimise ajal tüübikontrolli, mis välistab tüüpide mittevastavuse vead.
- Jõudlus: Tänu geneerilistele elementidele välistatakse väärtustüüpide kastidesse lisamise ja kastist eemaldamise toimingud, mis muudab List kiiremaks kui ArrayList.
- Juurdepääs: Elementidele pääseb ligi indeksi abil (alustades nullist). Indeksile juurdepääs võtab aega O(1).
——————————————————————————–
2. Sisemine struktuur ja mäluhaldus
List töötab sisemise massiivi peal. Elementide arvu ja mahutavuse erinevuse mõistmine on jõudluse optimeerimiseks kriitilise tähtsusega.
Count vs Capacity
| Omadus | Kirjeldus |
|---|---|
| Count | Tegelik elementide arv, mis hetkel nimekirjas on. |
| Capacity | Maksimaalne elementide arv, mida sisemine massiiv suudab salvestada ilma mälu ümberjaotamiseta. |
Suuruse muutmise protsess (Resizing)
Kui lisatavate elementide arv ületab praeguse mahutavuse, juhtub järgmine:
- Luuakse uus, suurema suurusega sisemine massiiv (tavaliselt kahekordistatakse; tänapäevastes .NET 6/7/8 implementatsioonides on algsuurus 4).
- Kõik elemendid kopeeritakse vanast massiivist uude.
- Vana massiiv asendatakse uuega. See protsess on kulukas ja O(n) keerukusega. Optimeerimise eesmärgil on soovitatav konstruktoris määrata algmaht, kui elementide ligikaudne arv on teada.
——————————————————————————–
3. Initsialiseerimine ja andmetega manipuleerimine
Initsialiseerimismeetodid
- Klassikaline meetod: Loo eksemplar ja kasuta meetodit Add().
- Kollektsiooni initsialiseerija: var list = new List { “A”, “B” }; (kasutusele võetud C# 3.0-s).
- Kollektsiooniavaldised (C# 12): List list = [“A”, “B”]; — moodne ja kokkuvõtlik süntaks.
Põhimeetodid
- Lisamine: Add(T item) lisab elemendi lõppu; AddRange(IEnumerable) lisab kollektsiooni; Insert(int index, T item) lisab elemendi määratud positsioonile.
- Eemaldamine:
- Remove(T item) — eemaldab objekti esimese esinemise.
- RemoveAt(int index) — eemaldab elemendi indeksi järgi.
- RemoveAll(Predicate) — eemaldab kõik tingimusele vastavad elemendid.
- Clear() — tühjendab loendi täielikult.
- Otsi:
- Contains(T item) — kontrollib elemendi olemasolu (tagastab bool-väärtuse).
- Find(Predicate) — tagastab esimese elemendi, mis vastab tingimusele või vaikeväärtusele (viitetüüpide puhul null).
- FindIndex(Predicate) — tagastab esimese vaste indeksi.
——————————————————————————–
4. Geneerilised ravimid ja toimivus
.NET Framework 2.0-s kasutusele võetud geneerilised tüübid võimaldavad teil defineerida andmestruktuure ilma kindlale tüübile pühendumata enne deklareerimist.
Geneeriliste ravimite kasutamise eelised:
- Kastidesse paigutamise/lahtipakkimise puudumine: ArrayListi kasutamisel on väärtustüübid (näiteks int) objekti sisse kastidesse pakitud, mis aeglustab jõudlust. List salvestab andmed aluseks olevasse täisarvumassiivi ilma teisendusteta.
- Tüübiturvalisus: Vead tuvastatakse kompileerimise ajal, mitte käitusajal.
- Sorteerimise efektiivsus: List sortimine võib olla kuni 70 korda kiirem kui samade andmetega ArrayListi sortimine, kuna puuduvad konstantsed tüübimuundused.
——————————————————————————–
5. LINQ ja parimad tavad
LINQ (Language Integrated Query) võimaldab teil teha keerukaid päringuid ühe rea loendite vastu.
Toimingute järjekord: kus vs. valik
LINQ-kõnede aheldamisel on järjekord jõudluse seisukohalt oluline:
- Soovitatav: Where().Select() — esmalt filtreeritakse andmed ja alles seejärel luuakse uued objektid (näiteks anonüümsed tüübid).
- Pole soovitatav: Select().Where() — süsteem loob uued objektid kõigi lähteloendi elementide jaoks, sealhulgas nende jaoks, mis hiljem filtreeritakse. See toob kaasa tarbetuid mälueraldusi.
Keerme ohutus
System.Collections.Generic nimeruumi List klass ei ole lõimekindel. Loendi samaaegne muutmine mitme lõime poolt põhjustab vigu. Mitmelõimeliste stsenaariumide korral kasutage System.Collections.Concurrent klasse (näiteks ConcurrentBag või ConcurrentQueue).
——————————————————————————–
6. Viktoriin
Juhised: Vasta küsimustele juhendis oleva teabe abil. Soovitatav vastuse pikkus on 2-3 lauset.
- Mis on peamine erinevus Count ja Capacity vahel List klassis?
- Miks on List kiirem kui ArrayList väärtustüüpide (nt int) salvestamisel?
- Kirjeldage protsessi, mis toimub List sees, kui kutsutakse välja Add() meetod ja sisemine massiiv saab ruum otsa.
- Mis on algse mahutavuse määramise eelis List konstruktoris?
- Mis vahe on Remove() ja RemoveAt() meetoditel?
- Miks on LINQ kasutamisel eelistatav kasutada Where meetodit enne Select meetodit?
- Mida tagastab Find(Predicate) meetod, kui loend ei sisalda elementi, mis vastab määratud tingimusele?
- Kas List on sobiv valik mitmelõimeliste keskkondade jaoks, kus mitu lõime kirjutavad andmeid samaaegselt? Miks?
- Milline on Big O ajaline keerukus List elemendile indeksi abil juurdepääsul ja kuidas see võrdub LinkedListiga?
- Millised uued loendi initsialiseerimise funktsioonid on C# 12-s saadaval?
——————————————————————————–
7. Vastuste võti
- Count kajastab loendisse lisatud elementide tegelikku arvu, samas kui Capacity (maht) on reserveeritud sisemise massiivi suurus. Capacity (maht) on alati suurem või võrdne Count-iga ja selle ületamine viib mälu ümberjaotamisele.
- List on üldine kogum, seega töötab see otse konkreetsete tüüpidega. See välistab vajaduse kastideks jagamise/lahtipakkimise järele, mis on vajalik ArrayListis, kus kõik elemendid teisendatakse tüübiobjektiks.
- Sisemiselt loob List uue massiivi, mis on tavaliselt kaks korda suurem kui eelmine. Seejärel kopeeritakse kõik olemasolevad elemendid uude massiivi, vana massiiv kustutatakse ja uus element lisatakse esimesse saadaolevasse pessa.
- Algse mahutavuse määramine hoiab ära mitu kallist massiivi suuruse muutmise ja andmete kopeerimise toimingut. See parandab oluliselt jõudlust olukordades, kus lisatavate elementide arv on ette teada.
- Meetod Remove(T item) leiab loendist konkreetse objekti esimese esinemise ja eemaldab selle, tagastades väärtuse true (tõene) või false (väär). Meetod RemoveAt(int index) eemaldab elemendi selle numbrilise positsiooni järgi. Kui indeks on vahemikust väljas, visatakse erand.
- Filtreerimine ahela alguses oleva Where-ga vähendab järgneva Select-meetodi poolt töödeldavate elementide arvu. See vähendab mälu eraldamist, kuna uued objektid luuakse ainult nende elementide jaoks, mis filtri läbivad.
- Meetod tagastab tüübi T vaikeväärtuse. Viitetüüpide puhul on see null ja väärtustüüpide (nt int) puhul tagastab see nende vaikeväärtuse (nt 0).
- Ei, List ei ole kirjutamisoperatsioonide jaoks lõimekindel. Mitmelõimelises keskkonnas ohutuks tööks ilma käsitsi sünkroniseerimiseta (nt lukkude kaudu) kasutage System.Collections.Concurrent nimeruumi kollektsioone.
- Elemendile indeksi järgi List-s juurdepääs võtab konstantse O(1) aja, kuna see on massiivioperatsioon. LinkedListis nõuab indeksi järgi juurdepääs loendi algusest peale elementide üle itereerimist, mis võtab lineaarse O(n) aja.
- C# 12 tutvustas “kogumi avaldisi”, mis võimaldavad loendit initsialiseerida nurksulgude abil, näiteks List nimed = [“üks”, “kaks”];. See süntaks on kokkuvõtlikum ja ühtlustatud erinevate kogumitüüpide vahel.
——————————————————————————–
8. Essee küsimused
- Analüüsige geneeriliste struktuuride mõju .NET-i koodi jõudlusele ja turvalisusele. Võrrelge List-d selle mitte-geneerilise eelkäija ArrayListiga.
- Arutage List sisemälu haldusmehhanisme. Millistel juhtudel võib automaatne suuruse muutmine muutuda rakenduse kitsaskohaks?
- Võrrelge List-d ja LinkedList-d. Kirjeldage kasutusjuhtumeid, kus ühel andmestruktuuril oleks teise ees selge eelis.
- Avastage LINQ-päringute optimeerimise põhimõtted kollektsioonide suhtes. Miks on laisa hindamise ja meetodite kutsumisjärjekorra (Where, Select, OrderBy) mõistmine arendaja jaoks oluline?
- Mõelge lõimede turvalisuse küsimusele kollektsioonidega töötamisel. Miks pole standardne List andmevõidujooksu eest kaitstud ja milliseid alternatiive pakub .NET paralleelprogrammeerimiseks?
——————————————————————————–
9. Põhimõistete sõnastik
| Termin | Määratlus |
|---|---|
| Generics (Обобщения) | Koodimallid, mis võimaldavad määratleda tüübistatud andmestruktuure ilma konkreetse tüübiga arendusajal fikseeritud sidumiseta. |
| Boxing (Pakendamine) | Väärtustüübi (nt int) teisendamine object tüübiks, mis nõuab mälu eraldamist virnas (Heap). |
| Unboxing (Lahtipakkimine) | Vastupidine protsess, kus objektist eraldatakse väärtustüüp. |
| Predicate (Predikaat) | Delegaat (funktsioon), mis võtab objekti ja tagastab loogilise väärtuse (true/false), kasutatakse otsimiseks või filtreerimiseks. |
| LINQ (Language Integrated Query) | C# tööriistade komplekt deklaratiivsete päringute kirjutamiseks andmete kohta otse koodis. |
| Immutable (Muutmatu) | Andmestruktuuri omadus, mida ei saa muuta pärast loomist (nt List filtratsioon LINQ kaudu loob sageli uue kogumi, ilma originaali muutmata). |
| Linear Search (Lineaarne otsing) | Elemendi otsimise meetod, kus kogumit läbivalt järjestikuse läbimisega otsitakse väärtust (O(n)), kasutusel nt Find meetodites. |
| Enumeration (Peremeerimine) | Protsess, kus kogumi kõik elemendid läbivad järjestikku, tavaliselt foreach tsükli abil. |
| Namespace (Nimeruum) | Konteiner seotud klasside ja tüüpide organiseerimiseks (nt System.Collections.Generic). |
| Strongly Typed (Tugevalt tüübistatud) | Kogumi omadus, mis tagab, et see võib sisaldada ainult kindla tüübi objekte. |