Exemple tipice de tablouri sunt vectorul și matricea din
matematică. Un vector este o colecție indexată de componente
cu un singur indice. De exemplu x=[x0, x1,
... , xn-1] este un vector cu n componente.
Componentele au același "nume" cu vectorul, dar se disting prin indici,
care specifică poziția componentei respective în cadrul tabloului. În
limbajul Java, la fel ca în limbajele C/C++, indicii încep de la 0.
Întrucât componentele vectorului sunt dispuse pe o singură direcție în
spațiu, spunem că este un tablou unidimensional. La nivel
conceptual, se consideră că tabloul ocupă o zonă compactă de memorie, în
care componentele sale sunt așezate în ordinea crescătoare a indicilor,
din care cauză mai este numit și masiv.
Matricea este un tablou bidimensional.
Componentele matricei sunt ordonate pe două direcții în spațiu, iar
poziția fiecărei componente este indicată prin doi indici: primul
specifică linia, iar al doilea coloana în care se găsește componenta
respectivă. Iată un exemplu de matrice cu 4 linii și 5 coloane:
În acest exemplu, numele matricei, ca și numele fiecărui element al ei, este a. Poziția componentei în cadrul matricei este specificată prin cei doi indici. Conform convenției de indexare din limbajul Java, indicii incep de la zero. Pot exista și tablouri cu mai mult de două dimensiuni. Astfel, un tablou tridimensional poate fi imaginat ca un volum (o carte), având mai multe pagini, fiecare pagină fiind un tablou bidimensional (o matrice). În acest caz, primul indice specifică linia, al doilea - coloana, iar al treilea - pagina în care se găsește componenta respectivă. În mod similar, un tablou cu patru dimensiuni poate fi privit ca o serie de volume; fiecare componentă, în acest caz, are patru indici, cel de al patrulea fiind numărul volumului în cadrul seriei. Putem, desigur, continua raționamentul și pentru tablouri cu mai mulți indici.
|
Tipul tabloului coincide cu tipul componentelor sale. Componentele pot aparține unor tipuri de date primitive, sau unor clase.
Tabloul unidimensional este constituit dintr-un ansamblu de componente indexate (cu un singur indice), căruia i se asociază și o variabila de tip int numita length, care reprezintă lungimea tabloului (numărul de componente). Indicii elementelor de tablou sunt cuprinși în intervalul [0, length-1]. Utilizarea unui indice situat în afara acestui interval generează o excepție.
Întrucât tablourile sunt obiecte, pentru indicarea lor în program se folosesc variabile referință.
Inițializarea tablourilor unidimensionale se poate
face, de asemenea, în două moduri:
a/ indicând valorile componentelor tabloului, separate prin virgule
și cuprinse între acolade, ca în exemplele următoare:
int a=27, b=-15, c[]={-3,72,-21},d=-5,e[]={231,-98};
String s1="un sir", ts1[]={"sirul 0", "sirul 1", "sirul 2"},
s2="alt sir";
float[] u={-1.24076f, 0.03254f, 27.16f}, v={2.7698E-12f,
-3.876e7f};
Remarcăm că în ultima declarație nu s-au mai pus paranteze după numele
variabilelor, deoarece ele apar după numele tipului.
b/ folosind operatorul new, urmat de numele tipului sau al clasei,
însoțit de dimensiunea tabloului (numărul de elemente din tablou)
scrisă între paranteze drepte, ca în exemplele următoare:
double aa[]=new double[3];
String str[]=new String[2];
În primul caz, se alocă în memorie spațiu pentru un tablou cu 3
componente de tip double, iar variabilei aa i se dă ca
valoare referința la acest tablou. În al doilea caz, se alocă în
memorie un tablou de variabile referință la obiecte din clasa String,
iar variabilei str i se dă ca valoare referința la
acest tablou.
Inițializarea unei variabile referință la tablou cu componente
aparținând unei anumite clase se poate face atât cu tablouri din clasa
respectivă, cât și din clase descendente ale acesteia. De exemplu, în
declarația
Object tab1[]=new Object[2], tab2[]=new String[3],
tab3[]={"aaa","bbb","ccc"};
variabila tab1 este initializata cu o referință la un tablou
de componente din clasa Object, în timp ce variabilele tab2 și
tab3 sunt inițializate cu referințe la tablouri de șiruri, clasa String(ca
orice alta clasă) fiind descendentă a clasei Object.
În programul din fișierul InitTab1.java se testează declarațiile și inițializările de mai sus și altele similare și se afișează valorile componentelor tablourilor inițializate. Se observă că, în cazul folosirii operatorului new pentru alocarea de tablouri, componentele acestora se inițializează la valorile lor implicite: 0 pentru date numerice și null pentru obiecte.
Utilizarea tablourilorComponentele tablourilor pot fi utilizate ca orice variabile simple. Referința la o componentă de tablou se face prin numele tabloului, insoțit de indicele componentei pus între paranteze drepte. De exemplu, u[3] este componenta de indice 3 a tabloului u, iar aa[i] este componenta de indice i a tabloului aa. Indicele poate fi orice expresie de tip întreg, cu condiția ca valoarea acesteia să nu iasa din cadrul domeniului de indici admis pentru tabloul respectiv.Un exemplu de utilizare a variabilelor indexate s-a dat deja în programul din fișierul InitTab1.java , când au fost afișate valorile componentelor tablourilor. Alte exemple se dau în programul din fișierul Tab1.java. Este instructiv să urmărim în figurile următoare cum evoluează datele din memorie în timpul executării acestui program.
- Fig. 1 - În figura 1 sunt reprezentate datele din memorie după
executarea instrucțiunilor Remarcăm deosebirea importantă dintre tabloul de tip double și cel de tip String. Primul dintre ele are drept componente date de tip primitiv. În consecință, "celulele" tabloului conțin chiar valorile de tip double ale componentelor corespunzătoare. În schimb, cel de al doilea este un tablou de obiecte din clasa String, deci componentele lui sunt, de fapt, variabile referință la obiecte String, iar aceste obiecte sunt reprezentate în memorie separat. În ambele cazuri, componentele tabloului sunt tratate ca niște variabile al căror tip este corespunzător declarației. Știm însă că variabilele de tipuri primitive au ca valori chiar date primitive, în timp ce pentru obiecte se folosesc variabile referință. În figura 2 este reprezentată situația creeată după ce
s-au executat instrucțiunile de atribuire
- Fig. 2 - Întrucât variabilele referință a[] și b[] indică același tablou, este normal ca valorile componentelor a[i] sunt și acum aceleași cu ale componentelor b[i], ceeace se constată și din afișarea prin program a datelor respective. În figura 3 este reprezentată situația creată după executarea instrucțiunii b=new double[4].
- Fig. 3 - Prin operatorul new s-a alocat în memorie un nou tablou cu 4 componente double, iar lui b[] i s-a dat ca valoare referința la acest tablou. Imediat după inițializare componentele noului tablou au valoarea zero, deoarece aceasta este valoarea implicită pentru tipurile de date numerice. În schimb, valoarea variabilei referință a[] a ramas aceeași, pe care a avut-o anterior. Acestor componente putem sa le dăm acum valori prin program. În același program se testează și situația în care se încearcă accesul la tabloul b[] cu un indice care iese din domeniul admis [0 ... b.lenght-1]. Se constată că se produce excepția java.lang.ArrayIndexOutOfBoundsException. Conversii de tip pentru referințe la tablouriTablourile cu componente aparținând unor tipuri de date primitive sunt considerate că sunt obiecte ale unor clase cu numeletip[] De exemplu, un tablou cu componente double (deci care a fost declarat ca double[]), aparține clasei double[], care este descendentă a clasei Object (nu a clasei Object[], care conține tablourile de obiecte). În mod asemanător, un tablou cu componente dintr-o anumită Clasa este considerat ca aparținând clasei Clasa[] care, de asemenea, este descendenta a clasei Object. De exemplu, daca s-au făcut declarațiile int a[]={54, 23, -17}, b[]; String str1[]={"abc","def"}, str2[], str3[]; Object ob1, ob3, tob1[]; sunt permise fara conversie explicită atribuiri de forma: tob1=str1; ob1=a; ob3=str1; În primul caz, tob1 este o referință la un tablou cu componente din clasa Object, iar str1 este referință la un tablou cu componente din clasa String, care este descendenta a clasei Object. În următoarele două cazuri, ob1 si ob3 sunt referințe la Object, iar tablourile a[] și str1[] aparțin claselor int[] și, respectiv, String[], care sunt și ele descendente ale clasei Object. În schimb, atribuirile următoare
necesită conversie explicită (prin cast), deoarece se fac de la
superclasă la clasă: Pentru a face referință la componente din tablourile referite de variabilele ob1 sau ob3 este necesară, de asemenea, conversie explicită, deoarece ob1 siob3 nu au fost declarate ca tablouri. Se va scrie deci: ((int[])ob1).length, ((int[])ob1)[k], ((String[])ob3).length, ((String[])ob3)[j]. Nu trebuie, insa, facuta conversie explicită în cazul componentelor tabloului referit prin tob1[], deoarece vsrisbila tob1 a fost declarată ca referință la tablou, iar clasa Object este superclasă a clasei String. Este, deci permisă referința tob1[k]. Exemplele de mai sus, si altele, sunt
testate în programul din fișierul ConvTip1.java.
În același program, se testează și numele claselor-tablouri întoarse de
metoda getName() a clasei Class.
Explicarea codificărilor respective este dată în documentația java API,
la descrierea acestei metode. |