This shows you the differences between two versions of the page.
|
poo:breviare:breviar-07 [2025/11/17 13:39] george.tudor1906 |
poo:breviare:breviar-07 [2025/11/19 09:25] (current) george.tudor1906 |
||
|---|---|---|---|
| Line 2: | Line 2: | ||
| === Colecții, iteratori, genericitate === | === Colecții, iteratori, genericitate === | ||
| - | |||
| - | --- | ||
| === 1. Colecții === | === 1. Colecții === | ||
| == 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 13: | Line 13: | ||
| Clasele și interfețele pentru lucrul cu colecții se află în pachetul **java.util**. Ierarhia pornește, pentru colecții propriu-zise, de la interfața **Collection**, care definește operațiile de bază (adăugare, eliminare, căutare, iterare). Din //Collection// derivă **trei ramuri principale**: | Clasele și interfețele pentru lucrul cu colecții se află în pachetul **java.util**. Ierarhia pornește, pentru colecții propriu-zise, de la interfața **Collection**, care definește operațiile de bază (adăugare, eliminare, căutare, iterare). Din //Collection// derivă **trei ramuri principale**: | ||
| - | * **List** – colecții ordonate și indexate, care permit duplicate; implementările uzuale sunt ArrayList (acces aleator rapid) și LinkedList (inserări/ștergeri eficiente cu iteratorul). Vector și Stack apar în bibliografie, dar sunt considerate tipuri legacy (fac parte din limbaj încă de la primele versiuni); pentru comportament de stivă/coadă se preferă azi Deque (ex. ArrayDeque). | + | * **List** - colecții ordonate și indexate, care permit duplicate; implementările uzuale sunt ArrayList (acces aleator rapid) și LinkedList (inserări/ștergeri eficiente cu iteratorul). Vector și Stack apar în bibliografie, dar sunt considerate tipuri legacy (fac parte din limbaj încă de la primele versiuni); pentru comportament de stivă/coadă se preferă azi Deque (ex. ArrayDeque). |
| - | * **Set** – mulțimi fără duplicate (în sens matematic); aici întâlnim HashSet (rapid, neordonat), LinkedHashSet (păstrează ordinea inserării) și TreeSet (menține elementele ordonate). Ordonarea formală este surprinsă de interfața SortedSet, care extinde Set și cere o ordine „naturală” sau dată de un Comparator, TreeSet fiind implementarea clasică. | + | * **Set** - mulțimi fără duplicate (în sens matematic); aici întâlnim HashSet (rapid, neordonat), LinkedHashSet (păstrează ordinea inserării) și TreeSet (menține elementele ordonate). Ordonarea formală este surprinsă de interfața SortedSet, care extinde Set și cere o ordine „naturală” sau dată de un Comparator, TreeSet fiind implementarea clasică. |
| - | * **Queue** / **Deque** – structuri de tip coadă (eventual cu două capete). Implementările standard sunt ArrayDeque (coadă/stivă eficientă) și PriorityQueue (ordonare după prioritate, nu după inserare). | + | * **Queue** / **Deque** - structuri de tip coadă (eventual cu două capete). Implementările standard sunt ArrayDeque (coadă/stivă eficientă) și PriorityQueue (ordonare după prioritate, nu după inserare). |
| - | Separat de Collection se află ierarhia **Map**, care gestionează perechi cheie - valoare. Cheile sunt unice, iar fiecare cheie mapează exact o valoare. Implementările cele mai folosite sunt **HashMap** (rapid, fără ordine), **LinkedHashMap** (menține ordinea inserării sau a accesului – util, de exemplu, pentru cache LRU - Least Recently Used) și **TreeMap** (chei ordonate). Interfața **SortedMap** extinde Map cu operații specifice ordinii - în practică, TreeMap este implementarea reprezentativă. Hashtable este o variantă veche, sincronizată, păstrată pentru compatibilitate, dar în cod modern se preferă HashMap (sau ConcurrentHashMap pentru acces concurent). | + | Separat de Collection se află ierarhia **Map**, care gestionează perechi cheie - valoare. Cheile sunt unice, iar fiecare cheie mapează exact o valoare. Implementările cele mai folosite sunt **HashMap** (rapid, fără ordine), **LinkedHashMap** (menține ordinea inserării sau a accesului - util, de exemplu, pentru cache LRU - Least Recently Used) și **TreeMap** (chei ordonate). Interfața **SortedMap** extinde Map cu operații specifice ordinii - în practică, TreeMap este implementarea reprezentativă. Hashtable este o variantă veche, sincronizată, păstrată pentru compatibilitate, dar în cod modern se preferă HashMap (sau ConcurrentHashMap pentru acces concurent). |
| <note important> | <note important> | ||
| Line 36: | Line 36: | ||
| public class Main { | public class Main { | ||
| public static void main(String[] args) { | public static void main(String[] args) { | ||
| - | // 1) Corect și sigur – generics | + | // 1) Corect și sigur - generics |
| List<String> nume = new ArrayList<>(); | List<String> nume = new ArrayList<>(); | ||
| nume.add("Ana"); | nume.add("Ana"); | ||
| Line 42: | Line 42: | ||
| String s1 = nume.get(0); // fără cast, sigur | String s1 = nume.get(0); // fără cast, sigur | ||
| - | // 2) Permis, dar nerecomandat – raw type (fără <T>) | + | // 2) Permis, dar nerecomandat - raw type (fără <T>) |
| List nespecificata = new ArrayList(); // WARNING: unchecked/unsafe | List nespecificata = new ArrayList(); // WARNING: unchecked/unsafe | ||
| nespecificata.add("Ana"); | nespecificata.add("Ana"); | ||
| Line 59: | Line 59: | ||
| } | } | ||
| </code> | </code> | ||
| - | |||
| - | --- | ||
| == 1.2 Liste (List) == | == 1.2 Liste (List) == | ||
| Line 123: | Line 121: | ||
| * Alegeți **ArrayList** când accentul este pe citire după index și **LinkedList** când aveți inserări/ștergeri locale cu iteratorul sau operații la ambele capete. | * Alegeți **ArrayList** când accentul este pe citire după index și **LinkedList** când aveți inserări/ștergeri locale cu iteratorul sau operații la ambele capete. | ||
| </note> | </note> | ||
| - | |||
| - | --- | ||
| == 1.3 Mulțimi (Set și SortedSet) == | == 1.3 Mulțimi (Set și SortedSet) == | ||
| - | `Set` modelează noțiunea de **mulțime** în sens matematic: | + | **Set** modelează noțiunea de **mulțime** în sens matematic: nu pot exista două elemente //o1//, //o2// într-un Set pentru care //o1.equals(o2)// este //true//. |
| - | + | ||
| - | <note important> | + | |
| - | Nu pot exista două elemente `o1`, `o2` într-un `Set` pentru care `o1.equals(o2)` este `true`. | + | |
| </note> | </note> | ||
| - | `Set` moștenește operațiile de bază din `Collection`, fără a introduce metode proprii. | + | Set moștenește operațiile de bază din Collection, fără a introduce metode proprii. Implementări uzuale: |
| - | Implementări uzuale: | + | |
| - | * **HashSet** – rapid, *neordonat*; | + | * **HashSet** - rapid, **neordonat**; |
| - | * **LinkedHashSet** – păstrează ordinea inserării; | + | * **LinkedHashSet** - păstrează ordinea inserării; |
| - | * **TreeSet** – menține elementele **ordonate**. | + | * **TreeSet** - menține elementele **ordonate**. |
| - | `SortedSet` reprezintă un `Set` în care elementele sunt păstrate în **ordine crescătoare**: | + | **SortedSet** reprezintă un Set în care elementele sunt păstrate în **ordine crescătoare**: |
| - | * fie după **ordinea naturală** (`Comparable`), | + | * fie după **ordinea naturală** (//Comparable//), |
| - | * fie după un `Comparator` furnizat la crearea colecției. | + | * fie după un //Comparator// furnizat la crearea colecției. |
| Implementarea standard de `SortedSet` este **TreeSet**. | Implementarea standard de `SortedSet` este **TreeSet**. | ||
| <note warning> | <note warning> | ||
| - | Într-un `SortedSet`, pentru orice două obiecte `o1`, `o2` ale colecției: | + | Într-un **SortedSet**, pentru orice două obiecte o1, o2 ale colecției, //o1.compareTo(o2)// sau //comparator.compare(o1, o2)// trebuie să fie **valid** (fără excepții), iar pentru ordinea naturală, elementele //null// **nu sunt permise**. |
| - | * `o1.compareTo(o2)` sau `comparator.compare(o1, o2)` trebuie să fie **valid** (fără excepții), | + | |
| - | * pentru ordinea naturală, elementele `null` **nu sunt permise** (`NullPointerException`). | + | |
| </note> | </note> | ||
| - | |||
| - | Începând cu Java 9 există mulțimi **imutabile** prin `Set.of(...)` și `Set.copyOf(...)`. | ||
| - | |||
| - | * `Set.of(...)` – creează direct o mulțime nemodificabilă (aruncă excepție dacă are duplicate); | ||
| - | * `Set.copyOf(...)` – copiază un set existent într-o versiune nemodificabilă. | ||
| <code java> | <code java> | ||
| Line 163: | Line 148: | ||
| class Example { | class Example { | ||
| - | public static void main(String[] args) { | + | public static void main(String[] args) { |
| - | // 1) HashSet – fără ordine, elimină duplicatele pe baza equals()/hashCode() | + | // 1) HashSet - fără ordine, elimină duplicatele pe baza equals()/hashCode() |
| - | Set<String> hs = new HashSet<>(); | + | Set<String> hs = new HashSet<>(); |
| - | Collections.addAll(hs, "Ana", "Ana", "Ion"); | + | Collections.addAll(hs, "Ana", "Ana", "Ion"); |
| - | System.out.println("HashSet: " + hs); // ex.: [Ana, Ion] | + | System.out.println("HashSet: " + hs); // ex.: [Ana, Ion] |
| - | ``` | + | // 2) LinkedHashSet - păstrează ordinea inserării |
| - | // 2) LinkedHashSet – păstrează ordinea inserării | + | Set<Integer> lhs = new LinkedHashSet<>(List.of(3, 1, 2, 1)); |
| - | Set<Integer> lhs = new LinkedHashSet<>(List.of(3, 1, 2, 1)); | + | System.out.println("LinkedHashSet: " + lhs); // [3, 1, 2] |
| - | System.out.println("LinkedHashSet: " + lhs); // [3, 1, 2] | + | |
| - | // 3) TreeSet – comparator: lungime, apoi lexicografic | + | // 3) TreeSet - comparator: lungime, apoi lexicografic |
| - | SortedSet<String> good = new TreeSet<>( | + | SortedSet<String> good = new TreeSet<>( |
| - | Comparator.comparingInt(String::length) | + | Comparator.comparingInt(String::length) |
| - | .thenComparing(Comparator.naturalOrder()) | + | .thenComparing(Comparator.naturalOrder()) |
| - | ); | + | ); |
| - | good.addAll(List.of("aa", "b", "bb")); | + | good.addAll(List.of("aa", "b", "bb")); |
| - | System.out.println("TreeSet ok: " + good); // [b, aa, bb] | + | System.out.println("TreeSet ok: " + good); // [b, aa, bb] |
| - | // Comparator problematic: compară DOAR lungimea -> unele elemente sunt excluse | + | // Comparator problematic: compară DOAR lungimea -> unele elemente sunt excluse |
| - | SortedSet<String> bad = new TreeSet<>(Comparator.comparingInt(String::length)); | + | SortedSet<String> bad = new TreeSet<>(Comparator.comparingInt(String::length)); |
| - | bad.addAll(List.of("aa", "bb")); // "bb" e ignorat: compare("aa","bb") == 0 | + | bad.addAll(List.of("aa", "bb")); // "bb" e ignorat: compare("aa","bb") == 0 |
| - | System.out.println("TreeSet problematic: " + bad); // [aa] | + | System.out.println("TreeSet problematic: " + bad); // [aa] |
| - | + | } | |
| - | // 4) Set imutabil (Java 9+) | + | |
| - | Set<String> roles = Set.of("ADMIN", "USER"); // nemodificabil | + | |
| - | System.out.println("Set.of: " + roles); | + | |
| - | // roles.add("GUEST"); // -> UnsupportedOperationException | + | |
| - | // Set.of("A", "A"); // -> IllegalArgumentException (duplicate la creare) | + | |
| } | } | ||
| - | ``` | + | </code> |
| - | + | ||
| - | } </code> | + | |
| - | --- | ||
| == 1.4 Dicționare (Map și SortedMap) == | == 1.4 Dicționare (Map și SortedMap) == | ||
| - | `Map` descrie structuri care asociază fiecărei **chei** o **valoare**: | + | **Map** descrie structuri care asociază fiecărei **chei** o **valoare**: |
| - | * **cheile sunt unice**; | + | * **cheile sunt unice**; |
| - | * valorile pot fi duplicate. | + | * valorile pot fi duplicate. |
| - | Ierarhia `Map` este separată de `Collection`. Operații tipice: | + | Ierarhia **Map** este separată de //Collection//. Operații tipice: |
| + | * inserare: //put(k, v)//; | ||
| + | * citire: //get(k)//, //getOrDefault(k, valoareImplicita)//; | ||
| + | * test de apartenență: //containsKey//, //containsValue//; | ||
| + | * eliminare: //remove(k)//; | ||
| + | * parcurgere: //entrySet()//, //keySet()//, //values()//. | ||
| - | * inserare: `put(k, v)`; | + | În practică, cele mai folosite implementări sunt: |
| - | * citire: `get(k)`, `getOrDefault(k, valoareImplicită)`; | + | |
| - | * test de apartenență: `containsKey`, `containsValue`; | + | |
| - | * eliminare: `remove(k)`; | + | |
| - | * parcurgere: `entrySet()`, `keySet()`, `values()`. | + | |
| - | Implementări uzuale: | + | * **HashMap** - rapid, fără ordine de iterare; permite o cheie //null// și valori //null//; |
| + | * **LinkedHashMap** - ca //HashMap//, dar păstrează ordinea inserării; | ||
| + | * **TreeMap** - menține cheile **ordonate** (natural sau prin //Comparator//); nu acceptă chei //null//; | ||
| + | * **Hashtable** - tip vechi; se preferă //HashMap// sau //ConcurrentHashMap//. | ||
| - | * **HashMap** – rapid, fără ordine de iterare; permite o cheie `null` și valori `null`; | + | **SortedMap** este un Map cu chei păstrate în **ordine crescătoare**; implementarea clasică este **TreeMap**. |
| - | * **LinkedHashMap** – ca `HashMap`, dar păstrează ordinea inserării (sau a accesului – util pentru cache LRU); | + | |
| - | * **TreeMap** – menține cheile **ordonate** (natural sau prin `Comparator`); nu acceptă chei `null`; | + | |
| - | * **Hashtable** – tip vechi, sincronizat; în cod modern se preferă `HashMap` sau `ConcurrentHashMap`. | + | |
| - | + | ||
| - | `SortedMap` este un `Map` cu chei păstrate în **ordine crescătoare**; implementarea clasică este `TreeMap`. | + | |
| - | + | ||
| - | În Java 9+, `Map.of(...)` și `Map.copyOf(...)` creează hărți **imutabile**. | + | |
| <code java> | <code java> | ||
| Line 229: | Line 202: | ||
| class MiniMapDemo { | class MiniMapDemo { | ||
| - | public static void main(String[] args) { | + | public static void main(String[] args) { |
| - | // 1) HashMap – fără ordine | + | // 1) HashMap - fără ordine |
| - | Map<String, Integer> freq = new HashMap<>(); | + | Map<String, Integer> freq = new HashMap<>(); |
| - | for (String w : List.of("ana", "are", "ana", "mere")) { | + | for (String w : List.of("ana", "are", "ana", "mere")) { |
| - | freq.merge(w, 1, Integer::sum); // new = 1, altfel +1 | + | freq.merge(w, 1, Integer::sum); // new = 1, altfel +1 |
| - | } | + | } |
| - | System.out.println("HashMap (fara ordine): " + freq); | + | System.out.println("HashMap (fara ordine): " + freq); |
| - | System.out.println("getOrDefault('banane', 0) = " | + | System.out.println("getOrDefault('banane', 0) = " |
| - | + freq.getOrDefault("banane", 0)); | + | + freq.getOrDefault("banane", 0)); |
| - | ``` | + | // 2) LinkedHashMap - păstrează ordinea inserării |
| - | // 2) LinkedHashMap – păstrează ordinea inserării | + | Map<Integer, String> lhm = new LinkedHashMap<>(); |
| - | Map<Integer, String> lhm = new LinkedHashMap<>(); | + | lhm.put(2, "B"); |
| - | lhm.put(2, "B"); | + | lhm.put(1, "A"); |
| - | lhm.put(1, "A"); | + | lhm.put(3, "C"); |
| - | lhm.put(3, "C"); | + | System.out.println("LinkedHashMap (ordine inserare): " + lhm.keySet()); // [2, 1, 3] |
| - | System.out.println("LinkedHashMap (ordine inserare): " + lhm.keySet()); // [2, 1, 3] | + | |
| - | // 3) TreeMap – chei ordonate (natural) | + | // 3) TreeMap - chei ordonate (natural) |
| - | Map<String, Integer> sorted = new TreeMap<>(freq); // sortează după cheia String | + | Map<String, Integer> sorted = new TreeMap<>(freq); // sortează după cheia String |
| - | System.out.println("TreeMap (chei ordonate): " + sorted); | + | System.out.println("TreeMap (chei ordonate): " + sorted); |
| - | // 4) Map imutabil (Java 9+) | + | // 4) Parcurgere eficientă cu entrySet() |
| - | Map<String, Integer> roles = Map.of("ADMIN", 1, "USER", 2); // nemodificabil | + | for (Map.Entry<String, Integer> e : sorted.entrySet()) { |
| - | System.out.println("Map.of: " + roles); | + | System.out.println(e.getKey() + " => " + e.getValue()); |
| - | // roles.put("GUEST", 3); // -> UnsupportedOperationException | + | } |
| - | + | ||
| - | // 5) Parcurgere eficientă cu entrySet() | + | |
| - | for (Map.Entry<String, Integer> e : sorted.entrySet()) { | + | |
| - | System.out.println(e.getKey() + " => " + e.getValue()); | + | |
| } | } | ||
| } | } | ||
| - | ``` | + | </code> |
| - | + | ||
| - | } </code> | + | |
| - | + | ||
| - | --- | + | |
| === 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 290: | 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 323: | 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 360: | 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 394: | 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 443: | 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 478: | 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> | + | |