This shows you the differences between two versions of the page.
|
poo:breviare:breviar-07 [2025/11/17 13:00] 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) == | ||
| - | Interfața **List**, pe lângă metodele moștenite din `Collection`, definește colecții: | + | Interfața **List**, pe lângă metodele moștenite din //Collection//, definește colecții **ordonate** și **indexate**, care **permit duplicate** și ale căror elemente pot fi accesate după **poziție** (index). În practică, cele mai folosite implementări sunt **ArrayList** și **LinkedList**. |
| - | * **ordonate** și **indexate**; | + | ArrayList oferă **acces aleator foarte rapid** la elemente, cu cost mai mare pentru inserări/ștergeri în interiorul listei. LinkedList stochează elementele într-o listă înlănțuită, ceea ce face **inserările și ștergerile** locale mai eficiente (folosind iteratorul), dar accesul la un element "din mijloc" este mai lent. LinkedList implementează și Deque, astfel că poate lucra comod cu elementele de la ambele capete (ex. addFirst, addLast). Pentru stivă/coadă se preferă ArrayDeque, iar pentru liste obișnuite ArrayList/LinkedList. |
| - | * care **permit duplicate**; | + | |
| - | * ale căror elemente pot fi accesate după **poziție** (index). | + | |
| - | + | ||
| - | Cele mai folosite implementări: | + | |
| - | + | ||
| - | * **ArrayList** – vector dinamic; acces aleator **rapid**, inserări/ștergeri în interiorul listei mai costisitoare; | + | |
| - | * **LinkedList** – listă înlănțuită; inserări/ștergeri locale mai eficiente (cu iterator), acces mai lent la elementele din „mijloc”; implementează și `Deque` (operare la ambele capete). | + | |
| - | + | ||
| - | Pentru comportament de **stivă/coadă** se preferă azi `ArrayDeque`, iar pentru liste obișnuite `ArrayList`/`LinkedList`. | + | |
| <code java> | <code java> | ||
| Line 81: | Line 70: | ||
| class Liste { | class Liste { | ||
| - | private final List<String> list1 = new ArrayList<>(); // ordonată, acces aleator rapid | + | private final List<String> list1 = new ArrayList<>(); // ordonată, acces aleator rapid |
| - | private final LinkedList<Integer> list2 = new LinkedList<>(); // listă + deque | + | private final LinkedList<Integer> list2 = new LinkedList<>(); // listă + deque |
| - | ``` | + | public static void main(String[] args) { |
| - | public static void main(String[] args) { | + | Liste obj = new Liste(); |
| - | Liste obj = new Liste(); | + | |
| - | // Operații de bază pe ArrayList<String> | + | // Operații de bază pe ArrayList<String> |
| - | obj.list1.add("Lab POO"); | + | obj.list1.add("Lab POO"); |
| - | obj.list1.add("Colectii"); | + | obj.list1.add("Colectii"); |
| - | obj.list1.add("Structuri de date"); | + | obj.list1.add("Structuri de date"); |
| - | if (obj.list1.contains("Colectii")) { | + | if (obj.list1.contains("Colectii")) { |
| - | System.out.println("Lista contine cuvantul"); | + | System.out.println("Lista contine cuvantul"); |
| - | } | + | } |
| - | // Parcurgere și ștergere în siguranță (fără ConcurrentModificationException) | + | // Parcurgere și ștergere în siguranță (fără ConcurrentModificationException) |
| - | Iterator<String> it = obj.list1.iterator(); | + | Iterator<String> it = obj.list1.iterator(); |
| - | while (it.hasNext()) { | + | while (it.hasNext()) { |
| - | String s = it.next(); | + | String s = it.next(); |
| - | System.out.println(s); | + | System.out.println(s); |
| - | it.remove(); // șterge elementul tocmai citit | + | it.remove(); // șterge elementul tocmai citit |
| - | } | + | } |
| - | // LinkedList<Integer> ca listă + deque (ambele capete) | + | // LinkedList<Integer> ca listă + deque (ambele capete) |
| - | obj.list2.addAll(Arrays.asList(1, 10, 20)); | + | obj.list2.addAll(Arrays.asList(1, 10, 20)); |
| - | obj.list2.addFirst(50); // capătul din stânga | + | obj.list2.addFirst(50); // capătul din stânga |
| - | obj.list2.addLast(17); // capătul din dreapta | + | obj.list2.addLast(17); // capătul din dreapta |
| - | // Modificare „pe loc” cu ListIterator (ex.: înmulțește numerele pare cu 10) | + | // Modificare „pe loc” cu ListIterator (ex.: înmulțește numerele pare cu 10) |
| - | ListIterator<Integer> li = obj.list2.listIterator(); | + | ListIterator<Integer> li = obj.list2.listIterator(); |
| - | while (li.hasNext()) { | + | while (li.hasNext()) { |
| - | int x = li.next(); | + | int x = li.next(); |
| - | if (x % 2 == 0) li.set(x * 10); | + | if (x % 2 == 0) li.set(x * 10); |
| - | } | + | } |
| + | |||
| + | // Afișare elemente (for-each) | ||
| + | for (Integer i : obj.list2) { | ||
| + | System.out.println(i); | ||
| + | } | ||
| - | // Afișare elemente (for-each) | + | // Sortare naturală (echivalent cu Collections.sort(list2)) |
| - | for (Integer i : obj.list2) { | + | obj.list2.sort(Comparator.naturalOrder()); |
| - | System.out.println(i); | + | System.out.println(obj.list2); |
| } | } | ||
| - | |||
| - | // Sortare naturală (echivalent cu Collections.sort(list2)) | ||
| - | obj.list2.sort(Comparator.naturalOrder()); | ||
| - | System.out.println(obj.list2); | ||
| } | } | ||
| - | ``` | + | </code> |
| - | + | ||
| - | } </code> | + | |
| <note tip> | <note tip> | ||
| - | * Declarați mereu tipul elementelor: `List<String>`, `List<Integer>`. | + | * Declarați mereu tipul elementelor: //List<String>//, //List<Integer>//. |
| - | * Pentru eliminări în timpul parcurgerii folosiți `Iterator.remove()` sau `removeIf(...)`. | + | * Pentru eliminări în timpul parcurgerii folosiți //Iterator.remove()// sau //removeIf(...)//. |
| - | * 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 174: | 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 | + | |
| - | Set<Integer> lhs = new LinkedHashSet<>(List.of(3, 1, 2, 1)); | + | |
| - | System.out.println("LinkedHashSet: " + lhs); // [3, 1, 2] | + | |
| - | // 3) TreeSet – comparator: lungime, apoi lexicografic | + | // 2) LinkedHashSet - păstrează ordinea inserării |
| - | SortedSet<String> good = new TreeSet<>( | + | Set<Integer> lhs = new LinkedHashSet<>(List.of(3, 1, 2, 1)); |
| - | Comparator.comparingInt(String::length) | + | System.out.println("LinkedHashSet: " + lhs); // [3, 1, 2] |
| - | .thenComparing(Comparator.naturalOrder()) | + | |
| - | ); | + | |
| - | good.addAll(List.of("aa", "b", "bb")); | + | |
| - | System.out.println("TreeSet ok: " + good); // [b, aa, bb] | + | |
| - | // Comparator problematic: compară DOAR lungimea -> unele elemente sunt excluse | + | // 3) TreeSet - comparator: lungime, apoi lexicografic |
| - | SortedSet<String> bad = new TreeSet<>(Comparator.comparingInt(String::length)); | + | SortedSet<String> good = new TreeSet<>( |
| - | bad.addAll(List.of("aa", "bb")); // "bb" e ignorat: compare("aa","bb") == 0 | + | Comparator.comparingInt(String::length) |
| - | System.out.println("TreeSet problematic: " + bad); // [aa] | + | .thenComparing(Comparator.naturalOrder()) |
| + | ); | ||
| + | good.addAll(List.of("aa", "b", "bb")); | ||
| + | System.out.println("TreeSet ok: " + good); // [b, aa, bb] | ||
| - | // 4) Set imutabil (Java 9+) | + | // Comparator problematic: compară DOAR lungimea -> unele elemente sunt excluse |
| - | Set<String> roles = Set.of("ADMIN", "USER"); // nemodificabil | + | SortedSet<String> bad = new TreeSet<>(Comparator.comparingInt(String::length)); |
| - | System.out.println("Set.of: " + roles); | + | bad.addAll(List.of("aa", "bb")); // "bb" e ignorat: compare("aa","bb") == 0 |
| - | // roles.add("GUEST"); // -> UnsupportedOperationException | + | System.out.println("TreeSet problematic: " + bad); // [aa] |
| - | // 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 240: | 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 301: | 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 334: | 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 371: | 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 405: | 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 454: | 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 489: | 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> | + | |