This shows you the differences between two versions of the page.
|
poo:breviare:breviar-07 [2025/11/17 20:32] george.tudor1906 |
poo:breviare:breviar-07 [2025/11/19 09:25] (current) george.tudor1906 |
||
|---|---|---|---|
| Line 6: | Line 6: | ||
| == 1.1 Interfața Collection și ierarhia colecțiilor == | == 1.1 Interfața Collection și ierarhia colecțiilor == | ||
| + | |||
| + | {{:poo:breviare:collection.png?600|}} | ||
| O **colecție** este un obiect care grupează mai multe elemente într-o singură unitate. Prin intermediul colecțiilor avem acces la diferite structuri de date: vectori dinamici, liste înlănțuite, stive, mulțimi, tabele de dispersie ș.a.m.d.. Colecțiile sunt folosite atât pentru **memorarea și manipularea datelor**, cât și pentru **transmiterea informațiilor între metode**. | O **colecție** este un obiect care grupează mai multe elemente într-o singură unitate. Prin intermediul colecțiilor avem acces la diferite structuri de date: vectori dinamici, liste înlănțuite, stive, mulțimi, tabele de dispersie ș.a.m.d.. Colecțiile sunt folosite atât pentru **memorarea și manipularea datelor**, cât și pentru **transmiterea informațiilor între metode**. | ||
| Line 231: | Line 233: | ||
| === 2. Iteratori și enumerări === | === 2. Iteratori și enumerări === | ||
| - | Enumerările și iteratorii descriu modalități de **parcurgere secvențială** a unei colecții, indiferent dacă este indexată sau nu. | + | Enumerările și iteratorii descriu modalități de **parcurgere secvențială** a unei colecții. În Java, parcurgerea se face în principal cu: |
| - | În Java modern, parcurgerea se face în principal cu: | + | * **Iterator** (și pentru liste, //ListIterator//), |
| - | + | * sintaxa **for-each**: //for (T e : colectie) { ... }//. | |
| - | * `Iterator` (și pentru liste, `ListIterator`), | + | |
| - | * sau cu sintaxa `for-each`: `for (T e : colectie) { ... }`. | + | |
| - | + | ||
| - | `Enumeration` și `Vector` sunt considerate tipuri **legacy** (istorice). | + | |
| - | + | ||
| - | --- | + | |
| == 2.1 Enumeration == | == 2.1 Enumeration == | ||
| - | `Enumeration` este o interfață veche pentru parcurgere. | + | **Enumeration** este o interfață veche pentru parcurgere. O mai întâlnim la //Vector// sau o putem obține din orice colecție prin //Collections.enumeration(...)//. În cod modern, se preferă //Iterator//. |
| - | + | ||
| - | O mai întâlnim la `Vector` sau o putem obține din orice colecție prin `Collections.enumeration(...)`. În cod modern, se preferă `Iterator`. | + | |
| <code java> | <code java> | ||
| Line 252: | Line 246: | ||
| class DemoEnumeration { | class DemoEnumeration { | ||
| - | public static void main(String[] args) { | + | public static void main(String[] args) { |
| - | List<Integer> list = List.of(3, 7, 0, 5); | + | List<Integer> list = List.of(3, 7, 0, 5); |
| - | Enumeration<Integer> en = Collections.enumeration(list); // din orice Collection | + | Enumeration<Integer> en = Collections.enumeration(list); // din orice Collection |
| - | ``` | + | while (en.hasMoreElements()) { |
| - | while (en.hasMoreElements()) { | + | int x = en.nextElement(); // Integer, nu Object (datorită generics) |
| - | int x = en.nextElement(); // Integer, nu Object (datorită generics) | + | System.out.println(x); |
| - | System.out.println(x); | + | } |
| } | } | ||
| - | } | ||
| - | ``` | ||
| - | } </code> | + | } |
| + | </code> | ||
| - | --- | ||
| == 2.2 Iterator == | == 2.2 Iterator == | ||
| - | `Iterator` oferă metodele: | + | **Iterator** oferă metodele: |
| - | + | * **hasNext()** - mai există elemente?; | |
| - | * `hasNext()` - mai există elemente? | + | * **next()** - returnează elementul următor; |
| - | * `next()` - returnează elementul următor; | + | * **remove()** - șterge ultimul element returnat de next(). |
| - | * `remove()` - șterge ultimul element returnat de `next()`. | + | |
| <note important> | <note important> | ||
| - | Dacă modificați colecția în timpul parcurgerii, folosiți **`iterator.remove()`** (sau, mai simplu, `removeIf(...)` pe colecție). | + | Dacă modificați colecția în timpul parcurgerii, folosiți **iterator.remove()** (sau, mai simplu, //removeIf(...)// pe colecție). |
| - | + | </note> | |
| - | Apeluri de tipul `list.remove(e)` direct într-un `for-each` pot produce `ConcurrentModificationException`. </note> | + | |
| <code java> | <code java> | ||
| Line 285: | Line 275: | ||
| class DemoIterator { | class DemoIterator { | ||
| - | public static void main(String[] args) { | + | public static void main(String[] args) { |
| - | List<String> l = new ArrayList<>(List.of("ana", "bad", "ion", "bogdan")); | + | List<String> l = new ArrayList<>(List.of("ana", "bad", "ion", "bogdan")); |
| - | ``` | + | // Variantă modernă: removeIf |
| - | // Variantă modernă: removeIf | + | l.removeIf(s -> s.length() == 4); // șterge elementele cu 4 litere |
| - | l.removeIf(s -> s.length() == 4); // șterge elementele cu 4 litere | + | |
| - | // Echivalent cu iterator.remove() | + | // Echivalent cu iterator.remove() |
| - | Iterator<String> it = l.iterator(); | + | Iterator<String> it = l.iterator(); |
| - | while (it.hasNext()) { | + | while (it.hasNext()) { |
| - | String s = it.next(); | + | String s = it.next(); |
| - | if (s.startsWith("b")) it.remove(); // sigur | + | if (s.startsWith("b")) it.remove(); // sigur |
| - | } | + | } |
| - | System.out.println(l); | + | System.out.println(l); |
| + | } | ||
| } | } | ||
| - | ``` | + | </code> |
| - | + | ||
| - | } </code> | + | |
| - | + | ||
| - | --- | + | |
| == 2.3 ListIterator (liste, ambele sensuri) == | == 2.3 ListIterator (liste, ambele sensuri) == | ||
| - | `ListIterator` extinde `Iterator` și oferă în plus: | + | //ListIterator// extinde Iterator și oferă în plus: |
| - | * navigare înapoi: `previous()`; | + | * navigare înapoi: //previous()//; |
| - | * poziții: `nextIndex()`, `previousIndex()`; | + | * poziții: //nextIndex()//, //previousIndex()//; |
| - | * inserare: `add(...)`; | + | * inserare: //add(...)//; |
| - | * înlocuire: `set(...)`. | + | * înlocuire: //set(...)//. |
| - | Este util când trebuie să modificați lista „pe loc” sau să o parcurgeți bidirecțional. | + | Este util când trebuie să modificați lista "pe loc" sau să o parcurgeți bidirecțional. |
| <code java> | <code java> | ||
| Line 322: | Line 308: | ||
| class DemoListIterator { | class DemoListIterator { | ||
| - | public static void main(String[] args) { | + | public static void main(String[] args) { |
| - | List<Integer> l = new ArrayList<>(List.of(0, 1, 2, 0, 3)); | + | List<Integer> l = new ArrayList<>(List.of(0, 1, 2, 0, 3)); |
| - | ``` | + | // 1) Înlocuire „pe loc”: 0 -> 10 |
| - | // 1) Înlocuire „pe loc”: 0 -> 10 | + | ListIterator<Integer> it = l.listIterator(); |
| - | ListIterator<Integer> it = l.listIterator(); | + | while (it.hasNext()) { |
| - | while (it.hasNext()) { | + | if (it.next() == 0) it.set(10); |
| - | if (it.next() == 0) it.set(10); | + | } |
| - | } | + | |
| - | // 2) Inserare după 1 | + | // 2) Inserare după 1 |
| - | ListIterator<Integer> it2 = l.listIterator(); | + | ListIterator<Integer> it2 = l.listIterator(); |
| - | while (it2.hasNext()) { | + | while (it2.hasNext()) { |
| - | if (it2.next() == 1) { // elementul curent 1; cursorul este după 1 (între 1 și 2) | + | if (it2.next() == 1) { // elementul curent 1; cursorul este după 1 (între 1 și 2) |
| - | it2.add(99); // inserează între 1 și 2 | + | it2.add(99); // inserează între 1 și 2 |
| - | break; | + | break; |
| + | } | ||
| } | } | ||
| - | } | ||
| - | System.out.println(l); // [10, 1, 99, 2, 10, 3] | + | System.out.println(l); // [10, 1, 99, 2, 10, 3] |
| + | } | ||
| } | } | ||
| - | ``` | + | </code> |
| - | + | ||
| - | } </code> | + | |
| <note warning> | <note warning> | ||
| - | Dacă parametrizăm colecția/iteratorul (ex. `List<String>`, `Iterator<String>`), metodele `next()`/`previous()` întorc direct **tipul elementului** și nu mai avem nevoie de cast. | + | Dacă parametrizăm colecția/iteratorul (ex. //List<String>//, //Iterator<String>//), metodele next()/previous() întorc direct **tipul elementului** și nu mai avem nevoie de cast. |
| - | Dacă folosim raw types (ex. `List`, `Iterator`), `next()`/`previous()` întorc `Object` și conversia devine responsabilitatea programatorului - cu risc de `ClassCastException` la rulare. </note> | + | Dacă folosim raw types (ex. //List//, //Iterator//), next()/previous() întorc **Object** și conversia devine responsabilitatea programatorului - cu risc de //ClassCastException// la rulare. </note> |
| <code java> | <code java> | ||
| Line 356: | Line 340: | ||
| class ObservatieIterator { | class ObservatieIterator { | ||
| - | public static void main(String[] args) { | + | public static void main(String[] args) { |
| - | // 1) Parametrizat (recomandat): fără cast, sigur | + | // 1) Parametrizat (recomandat): fără cast, sigur |
| - | List<String> l = new ArrayList<>(List.of("ana", "ion")); | + | List<String> l = new ArrayList<>(List.of("ana", "ion")); |
| - | Iterator<String> it = l.iterator(); | + | Iterator<String> it = l.iterator(); |
| - | String s1 = it.next(); // String, nu Object | + | String s1 = it.next(); // String, nu Object |
| - | ListIterator<String> li = l.listIterator(l.size()); | + | ListIterator<String> li = l.listIterator(l.size()); |
| - | String last = li.previous(); // tot String | + | String last = li.previous(); // tot String |
| - | System.out.println("OK (generic): " + s1 + ", " + last); | + | System.out.println("OK (generic): " + s1 + ", " + last); |
| - | ``` | + | // 2) Neparametrizat (raw type): next()/previous() -> Object, necesită cast |
| - | // 2) Neparametrizat (raw type): next()/previous() -> Object, necesită cast | + | List raw = new ArrayList(); // WARNING: unchecked/raw type |
| - | List raw = new ArrayList(); // WARNING: unchecked/raw type | + | raw.add("text"); // compilează |
| - | raw.add("text"); // compilează | + | raw.add(10); // compilează (amestec de tipuri!) |
| - | raw.add(10); // compilează (amestec de tipuri!) | + | Iterator itr = raw.iterator(); // WARNING: raw |
| - | Iterator itr = raw.iterator(); // WARNING: raw | + | Object o1 = itr.next(); // "text" ca Object |
| - | Object o1 = itr.next(); // "text" ca Object | + | Object o2 = itr.next(); // 10 ca Object |
| - | Object o2 = itr.next(); // 10 ca Object | + | |
| - | // Castul e responsabilitatea ta; poate eșua la rulare: | + | // Castul e responsabilitatea ta; poate eșua la rulare: |
| - | try { | + | try { |
| - | String s2 = (String) o2; // ClassCastException (Integer -> String) | + | String s2 = (String) o2; // ClassCastException (Integer -> String) |
| - | System.out.println(s2); | + | System.out.println(s2); |
| - | } catch (ClassCastException ex) { | + | } catch (ClassCastException ex) { |
| - | System.out.println("Eroare la rulare (raw type): " + ex); | + | System.out.println("Eroare la rulare (raw type): " + ex); |
| + | } | ||
| } | } | ||
| } | } | ||
| - | ``` | + | </code> |
| - | + | ||
| - | } </code> | + | |
| - | + | ||
| - | --- | + | |
| === 3. Genericitate (generics) === | === 3. Genericitate (generics) === | ||
| - | Fără generics, o colecție **raw** acceptă obiecte de orice fel, iar la citire trebuie să facem conversii (`cast`). Codul devine greu de urmărit, iar amestecul de tipuri poate produce ușor `ClassCastException` la rulare. | + | Fără **generics**, o colecție **raw** acceptă obiecte de orice fel, iar la citire trebuie să facem conversii (//cast//). Codul devine greu de urmărit, iar amestecul de tipuri poate produce ușor //ClassCastException// la rulare. |
| Genericitatea rezolvă exact aceste probleme: | Genericitatea rezolvă exact aceste probleme: | ||
| - | + | * declarăm de la început **tipul elementelor**; | |
| - | * declarăm de la început **tipul elementelor**; | + | * compilatorul verifică și **interzice** inserarea altor tipuri. |
| - | * compilatorul verifică și **interzice** inserarea altor tipuri. | + | |
| <code java> | <code java> | ||
| Line 405: | Line 384: | ||
| int y = list.get(1); // tot fără cast | int y = list.get(1); // tot fără cast | ||
| - | // list.add("Text"); // eroare de compilare: tip incompatibil </code> | + | // list.add("Text"); // eroare de compilare: tip incompatibil |
| + | </code> | ||
| <note warning> | <note warning> | ||
| - | Dacă aveți un caz real în care elementele pot fi de mai multe tipuri, NU reveniți la raw types. | + | Dacă aveți un caz real în care elementele pot fi de mai multe tipuri, **NU** reveniți la raw types. Folosiți: |
| - | Folosiți: | + | * **List<Object>** și verificați cu `instanceof` înainte de cast, |
| - | * `List<Object>` și verificați cu `instanceof` înainte de cast, | + | |
| * sau proiectați o ierarhie comună (o interfață, o clasă părinte) și parametrizați lista cu acest **supertip**. | * sau proiectați o ierarhie comună (o interfață, o clasă părinte) și parametrizați lista cu acest **supertip**. | ||
| - | În toate celelalte situații folosiți colecții parametrizate (`List<Student>`, `Map<String, Integer>`) pentru **siguranță și claritate**. </note> | + | În toate celelalte situații folosiți colecții parametrizate (//List<Student>//, //Map<String, Integer>//) pentru **siguranță și claritate**. </note> |
| - | --- | ||
| === 4. equals() vs hashCode() === | === 4. equals() vs hashCode() === | ||
| - | Metoda `equals(Object)` stabilește **egalitatea logică** dintre două instanțe. Definiția egalității aparține modelului de date (ex.: doi studenți pot fi considerați egali prin CNP, sau prin combinație de câmpuri). | + | Metoda **equals(Object)** stabilește **egalitatea logică** dintre două instanțe. Definiția egalității aparține modelului de date (ex.: doi studenți pot fi considerați egali prin CNP, sau prin combinație de câmpuri). |
| - | Compararea unei instanțe non-null cu referința `null` produce întotdeauna rezultat de inegalitate. Pentru tipuri numerice în virgulă mobilă se recomandă `Double.compare` / `Float.compare`. | + | Metoda **hashCode()** returnează un **rezumat numeric** (int) al obiectului. Colecțiile pe bază de dispersie (//HashSet//, //HashMap//) folosesc acest rezumat pentru localizare rapidă. |
| - | + | ||
| - | Metoda `hashCode()` returnează un **rezumat numeric** (`int`) al obiectului. Colecțiile pe bază de dispersie (`HashSet`, `HashMap`) folosesc acest rezumat pentru localizare rapidă. | + | |
| <note important> | <note important> | ||
| - | * Dacă două obiecte sunt **egale** prin `equals`, atunci **trebuie** să aibă același `hashCode()`. | + | * Dacă două obiecte sunt **egale** prin //equals//, atunci **trebuie** să aibă același //hashCode()//. |
| - | * Inversul nu este obligatoriu (două obiecte diferite pot avea același hashCode). | + | |
| </note> | </note> | ||
| - | În `HashSet` și `HashMap`, operațiile de apartenență și unicitate funcționează astfel: | + | În **HashSet** și **HashMap**, operațiile de apartenență și unicitate funcționează astfel: |
| - | 1. colecția calculează `hashCode()` și alege „bucket-ul”; | + | - colecția calculează **hashCode()** și alege locul elementului; |
| - | 2. confirmă prezența prin `equals()`. | + | - confirmă prezența prin **equals()**. |
| <code java> | <code java> | ||
| Line 440: | Line 415: | ||
| class EqualsHashDemo { | class EqualsHashDemo { | ||
| - | ``` | + | // Varianta corectă: equals și hashCode folosesc ACELEAȘI câmpuri |
| - | // Varianta corectă: equals și hashCode folosesc ACELEAȘI câmpuri | + | static final class GoodStudent { |
| - | static final class GoodStudent { | + | private final String id; |
| - | private final String id; | + | GoodStudent(String id) { this.id = id; } |
| - | GoodStudent(String id) { this.id = id; } | + | |
| - | @Override public boolean equals(Object o) { | + | @Override |
| - | if (this == o) return true; | + | public boolean equals(Object o) { |
| - | if (!(o instanceof GoodStudent g)) return false; | + | if (this == o) return true; |
| - | return Objects.equals(id, g.id); | + | if (!(o instanceof GoodStudent g)) return false; |
| - | } | + | return Objects.equals(id, g.id); |
| + | } | ||
| - | @Override public int hashCode() { | + | @Override |
| - | return Objects.hash(id); | + | public int hashCode() { |
| + | return Objects.hash(id); | ||
| + | } | ||
| } | } | ||
| - | } | ||
| - | // Varianta greșită: equals suprascris, hashCode NU | + | // Varianta greșită: equals suprascris, hashCode NU |
| - | static final class BadStudent { | + | static final class BadStudent { |
| - | private final String id; | + | private final String id; |
| - | BadStudent(String id) { this.id = id; } | + | BadStudent(String id) { this.id = id; } |
| - | @Override public boolean equals(Object o) { | + | @Override |
| - | if (this == o) return true; | + | public boolean equals(Object o) { |
| - | if (!(o instanceof BadStudent b)) return false; | + | if (this == o) return true; |
| - | return Objects.equals(id, b.id); | + | if (!(o instanceof BadStudent b)) return false; |
| + | return Objects.equals(id, b.id); | ||
| + | } | ||
| + | // (fără hashCode) -> folosește Object.hashCode(), diferit pentru instanțe diferite | ||
| } | } | ||
| - | // (fără hashCode) -> folosește Object.hashCode(), diferit pentru instanțe diferite | ||
| - | } | ||
| - | public static void main(String[] args) { | + | public static void main(String[] args) { |
| - | // Caz corect | + | // Caz corect |
| - | GoodStudent a1 = new GoodStudent("42"); | + | GoodStudent a1 = new GoodStudent("42"); |
| - | GoodStudent a2 = new GoodStudent("42"); | + | GoodStudent a2 = new GoodStudent("42"); |
| - | System.out.println("a1.equals(a2) = " + a1.equals(a2)); // true | + | System.out.println("a1.equals(a2) = " + a1.equals(a2)); // true |
| - | System.out.println("a1.hashCode = " + a1.hashCode() | + | System.out.println("a1.hashCode = " + a1.hashCode() |
| - | + " | a2.hashCode = " + a2.hashCode()); // egale | + | + " | a2.hashCode = " + a2.hashCode()); // egale |
| - | Set<GoodStudent> good = new HashSet<>(); | + | Set<GoodStudent> good = new HashSet<>(); |
| - | good.add(a1); | + | good.add(a1); |
| - | good.add(a2); | + | good.add(a2); |
| - | System.out.println("HashSet<GoodStudent>.size = " + good.size()); // 1 | + | System.out.println("HashSet<GoodStudent>.size = " + good.size()); // 1 |
| - | // Caz greșit | + | // Caz greșit |
| - | BadStudent b1 = new BadStudent("42"); | + | BadStudent b1 = new BadStudent("42"); |
| - | BadStudent b2 = new BadStudent("42"); | + | BadStudent b2 = new BadStudent("42"); |
| - | System.out.println("b1.equals(b2) = " + b1.equals(b2)); // true | + | System.out.println("b1.equals(b2) = " + b1.equals(b2)); // true |
| - | System.out.println("b1.hashCode = " + b1.hashCode() | + | System.out.println("b1.hashCode = " + b1.hashCode() |
| - | + " | b2.hashCode = " + b2.hashCode()); // diferite | + | + " | b2.hashCode = " + b2.hashCode()); // diferite |
| - | Set<BadStudent> bad = new HashSet<>(); | + | Set<BadStudent> bad = new HashSet<>(); |
| - | bad.add(b1); | + | bad.add(b1); |
| - | bad.add(b2); | + | bad.add(b2); |
| - | System.out.println("HashSet<BadStudent>.size = " + bad.size()); // 2 (duplicate) | + | System.out.println("HashSet<BadStudent>.size = " + bad.size()); // 2 (duplicate) |
| - | System.out.println("bad.contains(new BadStudent(\"42\")) = " | + | System.out.println("bad.contains(new BadStudent(\"42\")) = " |
| - | + bad.contains(new BadStudent("42"))); // false | + | + bad.contains(new BadStudent("42"))); // false |
| + | } | ||
| } | } | ||
| - | ``` | + | </code> |
| - | + | ||
| - | } </code> | + | |