fel
le

Rekord alapú vektorok

Tegyük fel, hogy van egy egyszerű rekord típusunk, mondjuk az alábbi (a példa kedvéért):

class diak
{
   public string neve;
   public string neptun_kod;
   public int kreditek;
}
 

Szeretnénk kereken 30 diák adatait kezelő programot írni. Ha 30 diák kell, célszerűtlen 30 darab rekord változót deklarálni, holott valójában ez kellene:

diak d1 = new diak();
diak d2 = new diak();
diak d3 = new diak();
diak d4 = new diak();
..
diak d30 = new diak();
 

Helyette célszerű diak alaptípusú vektort létrehoznunk:

diak[] osztaly = new diak[30];
 

A vektorok referencia típusú változók, tehát egy osztaly változónk 4 byte, egy memóriacímet tartalmaz, mely magára a vektor területére mutat. A vektor területe 30 darab diak változónak felel meg, holott a diak változó maga is referencia típusú, darabja 4 byte. A vektor tehát 30x4 byte, 120 byte helyigényű, és 30 darab memóriacímet tartalmaz (kezdetben mindegyike null értékű).

1. ábra: A rekord vektor a memóriában

Vagyis egyelőre a vektorunk alkalmatlan a tényleges adattárolásra, hiszen egyelőre rekordból egyetlen egy sem áll rendelkezésre, mezők nincsenek még kialakítva. Azokat egyesével kell létrehozni:

// maga a vektor helyfoglalása
diak[] osztaly = new diak[30];
// a vektor elemek helyfoglalása
for(int i=0;i<osztaly.Length;i++)
{
   osztaly[i] = new diak();
}
 

Kicsit zűrzavarosan néz ki, de a memóriabeli helyzet szétszórt. Minden egyes vektorelem egy-egy memóriacím egy újabb területre a memóriában, ahol a tényleges rekord elhelyezkedik.

2. ábra: A vektor a memóriában a rekordokkal

Problémás lenne a vektor feltöltése az alábbi módon:

// maga a vektor helyfoglalása
diak[] osztaly = new diak[30];
// a vektor !! hibás !! feltöltése
diak d = new diak();
for(int i=0;i<osztaly.Length;i++)
{
   osztaly[i] = d;
}
 

Ez esetben ugyanis minden vektorelem ugyanazon memóriacímet kapná meg, tehát nem 30 különböző rekordunk lenne, hanem ugyanazon rekord szerepelne 30 esetben, így bármely vektorelemen keresztül ugyanazon rekordhoz férnénk hozzá.

3. ábra: A vektor rosszul feltöltött állapota

Munka a vektor elemeivel

Amennyiben van egy jól feltöltött vektorunk, amelyben rekordok vannak, a továbbiakban az alábbi szintaktikával tudjuk a rekordok mezőit elérni. Azt használjuk ki, hogy a referencia típusú változók közöti értékadás során csak a memóriacímet másoljuk le, vagyis a 'd' változóba bekerülő memóriacímen valójában az 'osztaly' vektor '0.' sorszámú rekordjához férünk hozzá:

diak d = osztaly[0];
d.neve = "Lajos";
d.neptun_kod = "NO99AA";
 

Ezt persze megtehetjük az extra 'd' változó nélkül is:

osztaly[0].neve = "Lajos";
osztaly[0].neptun_kod = "NO99AA";
 

Ciklusba foglalva:

for(int i=0;i<osztaly.Length;i++)
{
  diak d = osztaly[i];
  d.neve = Console.ReadLine();
  d.neptun_kod = Console.ReadLine();
  d.kreditek = int.Parse( Console.ReadLine() );
}
 

vagy:

for(int i=0;i<osztaly.Length;i++)
{
  osztaly[i].neve = Console.ReadLine();
  osztaly[i].neptun_kod = Console.ReadLine();
  osztaly[i].kreditek = int.Parse( Console.ReadLine() );
}
 

És, érdekes módon a foreach-el is megoldható:

foreach(diak d in osztaly)
{
  d.neve = Console.ReadLine();
  d.neptun_kod = Console.ReadLine();
  d.kreditek = int.Parse( Console.ReadLine() );
}
 

Ez utóbbi első ránézésre meglepő lehet, hiszen azt tanultuk a foreach-ről, hogy eközben a vektor (vagy lista) nem változhat meg, a 'd' elem csak olvasható, nem használhatjuk fel értékadó utasításban. Ugyanakkor vegyük észre, hogy maga a vektor jelenleg nem is változik, nem magára a 'd'-re vonatkozik az értékadó utasítás, hanem a 'd'-n keresztül a másodlagos memóriaterületre, ahol a mezők kerültek tárolásra.

Vagyis az alábbi foreach nem működne valóban:

// maga a vektor helyfoglalása
diak[] osztaly = new diak[30];
// a vektor elemek helyfoglalása (!! HIBÁS !!)
foreach(diak d in osztaly)
{
   d = new diak();
}
 

A vektor elemeinek feltöltését, a 30 diák helyfoglalását és a memóriacímek vektorba helyezését csak for ciklussal lehet megvalósítani (vagy while-al természetesen). A foreach azonban alkalmas arra, hogy maguk a vektorbeli rekordok mezőinek értékeit beállítsuk.

Rekord alapú listák

Amennyiben a rekord alapú vektorok minden csínja-bínja érthető és világos, úgy a vektor alapú listák működése már nem okoz meglepetést:

List<diak> csoport = new List<diak>();
for(int i=0;i<30;i++)
{
  diak d = new diak();
  csoport.Add( d );
}
 

vagy (ugyanaz az extra 'd' változó nélkül):

List<diak> csoport = new List<diak>();
for(int i=0;i<30;i++)
{
  csoport.Add( new diak() );
}
 

Rekord listával műveletvégzés

A rekordok helyfoglalása után van lehetőség a rekord mezőinek feltöltésére (hasonlóan a vektornál megismert módokon):

for(int i=0;i<csoport.Count;i++)
{
  osztaly[i].neve = Console.ReadLine();
  osztaly[i].neptun_kod = Console.ReadLine();
  osztaly[i].kreditek = int.Parse( Console.ReadLine() );
}
 

És megoldható foreach-el is:

foreach(diak d in csoport)
{
  d.neve = Console.ReadLine();
  d.neptun_kod = Console.ReadLine();
  d.kreditek = int.Parse( Console.ReadLine() );
}
 

A különbség csak abban rejlik, hogy itt .Count van a .Length helyett. A foreach-es megoldás esetén nincs különbség, mert ott ezt a foreach maga kezeli és rejti el.

Hernyák Zoltán
2012-12-21 16:03:40