Se afișează postările cu eticheta Set. Afișați toate postările
Se afișează postările cu eticheta Set. Afișați toate postările

marți, 31 martie 2026

Set. HashSet. LinkedHashSet. TreeSet - Set de probleme

Pentru efectuarea sarcinilor acestui laborator cercetați mai întâi reperele teoretice: https://musteatadidactic.blogspot.com/2026/03/set-hashset-linkedhashset-treeset.html

1. Testați în editorul de cod Java codul de la Exemplul 1. Efectuați următoarele modificări:
  • Creați o nouă listă de tip HashSet ce va conține elemente de tip întreg. 
  • Citiți 10 elemente de la tastatură. 
  • Afișați elementele citite la ecran însoțite de suma acestora.
2. Testați în editorul de cod Java codul de la Exemplul 4. Efectuați următoarele modificări:
  • Adăugați clasei Elev caracteristica IDNP care reprezintă codul personal al elevului. Modificați întreg codul (constructor, toString(), main()) astfel încât acesta să ruleze cu succes.
  • Modificați metodele equals() și hashCode() astfel încât acestea să nu admită elevi cu același IDNP în listă.
  • Citiți datele elevilor dintr-un fișier text.
3. Testați în editorul de cod Java codul de la Exemplul 5. Efectuați următoarele modificări:
  • Citiți datele dintr-un fișier în care se cunoaște că sunt 20 valori întregi unele dintre care se repetă.
  • Sortați elementele descrescător.
4. Testați în editorul de cod Java codul de la Exemplul 7. Efectuați următoarele modificări:
  • Adăugați clasei Elev câmpul grupa, care reprezintă grupa în care elevul studiază. Modificați întreg codul (constructor, toString(), main()) astfel încât acesta să ruleze cu succes.
  • Sortați elementele crescător după grupă și descrescător după notă.
5. Elaborați un program Java care va conține o listă de tip HashSet cu 10 valori. Afișați lista la ecran. Eliminați folosind instrucțiunea removeIf() toate numerele mai mari de 20.

6. Elaborați un program Java care va înscrie într-o listă elevii în ordinea în care au ridicat mâna să răspundă. Deci va trebuie să păstrezi ordinea participării la discuție și nu se dorește ca fiecare elev să fie înregistrat de două ori. Ce tip de date vei declara lista? Consultă tabelul din repere teoretice! Lista va avea tipul LinkedHashSet și va stoca elemente de tip Elev.

7. Se dorește o aplicație care va crea un clasament al elevilor participanți la competiție. Elaborați un program Java care va înscrie într-o listă elevii și punctajul acestora. Nu se permite elemente duplicat, iar elevii trebuie afișați automat în ordine descrescătoare punctajelor acumulate. Ce tip de date vei declara lista? Consultă tabelul din repere teoretice! Lista va avea tipul TreeSet, pentru că sortează automat elementele, și va stoca elemente de tip Elev.

Vă doresc o zi deosebit de frumoasă!
❤️

Set. HashSet. LinkedHashSet. TreeSet

După ce am studiat colecțiile de tip List, unde elementele sunt ordonate și pot apărea duplicate, este important să analizăm și o altă categorie esențială din cadrul colecțiilor Java — Set.

Spre deosebire de liste, structurile de tip
Set au o regulă foarte clară: nu permit elemente duplicate. Încercarea de a adăuga un element deja prezent în Set nu va avea efect. Această caracteristică le face extrem de utile în situațiile în care dorim să lucrăm cu date unice, cum ar fi eliminarea valorilor repetate sau verificarea rapidă a existenței unui element.

Un alt aspect important este că, în funcție de implementare, seturile pot:
  • să nu păstreze ordinea elementelor - clasa HashSet
  • să păstreze ordinea de inserare - clasa LinkedHashSet
  • sau să sorteze automat elementele - clasa TreeSet
În continuare, vom examina fiecare clasă separat, evidențiind modul de funcționare și situațiile în care este recomandată utilizarea acesteia.

Interfața Set pune la dispoziție un set de metode care permit gestionarea eficientă a colecțiilor fără elemente duplicate. Aceste metode oferă posibilitatea de a adăuga, elimina, verifica și parcurge elementele, indiferent de tipul concret de implementare utilizat. Cunoașterea acestor metode este extrem de importantă deoarece ele reprezintă baza lucrului cu toate tipurile de seturi (HashSet, LinkedHashSet, TreeSet). 
Astfel, odată înțelese, ele pot fi aplicate ușor în orice context, fără a fi necesară învățarea separată pentru fiecare clasă. Deoarece noi cunoaștem cu toate clasele care implementează o interfață conțin implementare pentru metodele interfeței.
Deci, interfața Set are următoarele metode principale:
  • boolean add(E element) - adaugă un element în set. Returnează true dacă setul nu conține deja elementul adăugat.
  • boolean contains(Object o)) - verifică dacă setul conține un anumit element.
  • boolean remove(Object o) - elimină un element din set. Returnează true dacă setul conținea elementul șters.
  • int size() - returnează numărul de elemente din set.
  • boolean isEmpty() - verifică dacă setul este gol (nu conține elemente).
  • void clear() - elimină toate elementele din set.
  • Iterator<E> iterator() - returnează un iterator peste elementele din set.
  • Object[] toArray() - returnează un tablou care conține toate elementele din set.
  • <T> T[] toArray(T[] a) - returnează un tablou care conține toate elementele din set, într-un tablou specificat.
  • boolean containsAll(Collection<?> c) - verifică dacă setul conține toate elementele dintr-o altă colecție.
  • boolean addAll(Collection<? extends E> c) - adaugă toate elementele dintr-o altă colecție în set.
  • boolean removeAll(Collection<?> c) - elimină toate elementele din set care sunt prezente și într-o altă colecție.
  • boolean equals(Object o) - verifică dacă setul este egal cu un alt obiect.
  • int hashCode() - returnează codul hash pentru set.
  • boolean removeIf(Predicate<? super E> filter) - elimină elementele care respectă o condiție. Vedeți expresiile Lambda aici https://musteatadidactic.blogspot.com/2026/02/expresiile-lambda.html
  • void forEach(Consumer<? super E> action)- parcurge fiecare element și aplică o acțiune. Acceptă ca parametru o expresie lambda.
Interfața Set nu poate fi utilizată direct, deoarece este doar un model. Pentru a lucra efectiv cu date, avem nevoie de o clasă concretă care să implementeze această interfață.
Una dintre cele mai frecvent utilizate implementări ale interfeței
Set este HashSet. Aceasta permite stocarea elementelor într-un mod rapid și eficient, utilizând mecanismul de hashing, și asigură în același timp unicitatea acestora.
În continuare, vom analiza clasa
HashSet, modul de funcționare și principalele operații disponibile.
Clasa
HashSet reprezintă o colecție în care elementele sunt organizate prin hashing, ceea ce permite un acces foarte rapid. Operațiile de adăugare, eliminare și căutare au, în medie, o complexitate de timp O(1), ceea ce le face potrivite pentru lucrul cu volume mari de date. Această clasă nu permite elemente duplicate și, în mod obișnuit, nu acceptă valori nule.
Constructori importanți:
  • HashSet() – creează un set gol, utilizând valorile implicite
  • HashSet(Collection<? extends E> c) – creează un set care conține elementele unei colecții existente
Exemplul 1 Creează o listă de tip HashSet ce va conține discipline, adaugă câteva elemente (fără duplicate), și le afișează folosind trei metode diferite de parcurgere, afișează și numărul total de elemente in listă.
import java.util.HashSet;
import java.util.Set;

public class Main{
public static void main(String[] args) {
Set<String> discipline = new HashSet<>();
discipline.add("Matematica");
discipline.add("Informatica");
discipline.add("Fizica");
discipline.add("Matematica");
System.out.println("\u001B[4m\033[1mAfișare directă\033[0m\u001B[0m");
System.out.println(discipline);
System.out.println("\u001B[4m\033[1m\nParcurgere cu for îmbunătățit\033[0m\u001B[0m");
for (String d : discipline) {
System.out.println(d);
}
System.out.println("\u001B[4m\033[1m\nParcurgere cu expresie lambda\033[0m\u001B[0m");
discipline.forEach(x -> System.out.println(x));
System.out.println("\u001B[4m\033[1m\nNumar elemente:\033[0m\u001B[0m " + discipline.size());
}}
Atenție la elementele care au fost adăugate în listă și la cele care au fost afișate! 👀 Ce observați ? Evident, elementele duplicat nu sunt adăugate în listă 👌 
Detalii despre clasa HashSet direct din sursa oficială https://docs.oracle.com/javase/8/docs/api/java/util/HashSet.html

Exemplul 2 Vom modifica exemplul de mai sus prin crearea unei liste de tip  ArrayList care permite elemente duplicat. Observați diferențele prin compararea rezultatelor.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> discipline = new ArrayList<>();
discipline.add("Matematica");
discipline.add("Informatica");
discipline.add("Fizica");
discipline.add("Matematica");
System.out.println("\u001B[4m\033[1m\nParcurgere cu expresie lambda\033[0m\u001B[0m");
discipline.forEach(x -> System.out.println(x));
System.out.println("\u001B[4m\033[1m\nNumar elemente:\033[0m\u001B[0m " + discipline.size());
}}
Clasa LinkedHashSet implementează interfeța Set care combină unicitatea elementelor cu menținerea ordinii de inserare. Practic, ea păstrează elementele exact în ordinea în care au fost adăugate, spre deosebire de HashSet, care nu garantează nicio ordine.
Această clasă folosește un hashing pentru a asigura unicitatea elementelor, adică nu permite duplicate și  folosește o listă dublu înlănțuită internă pentru a păstra ordinea de inserare. Oferă operații rapide de adăugare, eliminare și căutare, aproape de O(1), însă are un consum mai mare de memorie comparativ cu HashSet, din cauza listei dublu înlănțuite, adică este puțin mai lent decât HashSet la operații simple.
Constructori importanți: 
  • LinkedHashSet() - creează un set gol fără parametri. Menține ordinea de inserare.
  • LinkedHashSet(Collection<? extends E> c) - creează un set care conține toate elementele unei colecții existente. Elimină duplicatele automat.
Exemplul 3 Vom crea o listă de tip LinkedHashSet în care vom stoca elemente de tip Elev
import java.util.LinkedHashSet;
import java.util.Set;
class Elev {
private String nume;
private int nota;
public Elev(String nume, int nota) {
this.nume = nume;
this.nota = nota;
}
@Override
public String toString() {
return nume + " - Nota: " + nota;
}
}
public class Main {
public static void main(String[] args) {
Set<Elev> elevi = new LinkedHashSet<>();
elevi.add(new Elev("Ana", 9));
elevi.add(new Elev("Ion", 7));
elevi.add(new Elev("Maria", 10));
elevi.add(new Elev("Bogdan", 8));
elevi.add(new Elev("Ana", 9));
System.out.println("Lista elevilor:");
elevi.forEach(x -> System.out.println(x));
}
}

După cum observați, necătând la faptul că
LinkedHashSet nu premite elemente duplicat, exemplul nostru are un alt rezultat și avem două persoane cu aceleași valori, este din motiv că operatorul new() crează un nou obiect în sistem și acesta va avea codul hash diferit de obiectul anterior, iar limbajul Java compară implicit doar acest cod, nu și conținutul obiectelor. În acest caz este necesară supradefinirea metodelor equals() și hashCode().
Exemplul 4 Vom crea o listă de tip LinkedHashSet în care vom stoca elemente de tip Elev. Vom supradefini metodele equals() și hashCode() pentru a nu permite două obiecte cu același conținut. 
import java.util.LinkedHashSet;
import java.util.Set;
class Elev {
private String nume;
private int nota;
public Elev(String nume, int nota) {
this.nume = nume;
this.nota = nota;
}
@Override
public String toString() {
return nume + " - Nota: " + nota;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Elev)) return false;
Elev elev = (Elev) o;
return nota == elev.nota && nume.equals(elev.nume);
}
@Override
public int hashCode() {
return nume.hashCode() + nota;
}
}
public class Main {
public static void main(String[] args) {
Set<Elev> elevi = new LinkedHashSet<>();
elevi.add(new Elev("Ana", 9));
elevi.add(new Elev("Ion", 7));
elevi.add(new Elev("Maria", 10));
elevi.add(new Elev("Bogdan", 8));
elevi.add(new Elev("Ana", 9));
System.out.println("Lista elevilor:");
elevi.forEach(x -> System.out.println(x));
}}
În proiecte reale pentru a asigura unicitatea fiecărui elev în colecție, vom lua în considerare un element care îl identifică în mod unic, cum ar fi un ID sau un cod personal (IDNP), astfel încât să nu putem avea doi elevi cu același identificator, chiar dacă au aceeași notă sau același nume.
Clasa TreeSet este o implementare a interfeței NavigableSet, care stochează elementele sortate automat, deci și aici elemente duplicate nu sunt permise și nici
valorile null. Are la bază arbore binar de căutare. Operațiile de adăugare, ștergere și căutare au complexitate O(log n)
Constructori importanți: 
  • TreeSet() – creează un set gol cu ordonare naturală. 
  • TreeSet(Collection<? extends E> c) – creează un set cu elementele dintr-o colecție existentă și le sortează.
  • TreeSet(Comparator<? super E> comparator) – creează un set gol care folosește un comparator personalizat pentru sortare.
Exemplul 5 Vom crea o listă de tip TreeSet care va conține elemente întregi.
import java.util.*;
public class Main {
public static void main(String[] args) {
Set<Integer> numere = new TreeSet<>();
numere.add(5);
numere.add(2);
numere.add(8);
numere.add(3);
numere.add(5);
numere.add(1);
System.out.println("Numere sortate automat:");
for (Integer n : numere) {
System.out.print(n+" ");
}}}

După cum observați elementele au fost automat sortate în ordine crescătoare, iar duplicatele au fost omise. 
Exemplul  6 Vom crea o listă de tip TreeSet care va conține elemente întregi, dar vor fi sortate automat în ordine descrescătoare.
import java.util.*;
public class Main {
public static void main(String[] args) {
Set<Integer> numere = new TreeSet<>(Comparator.reverseOrder());
numere.add(5);
numere.add(2);
numere.add(8);
numere.add(3);
numere.add(5);
numere.add(1);
System.out.println("Numere sortate descrescător:");
for (Integer n : numere) {
System.out.print(n+" ");
}}}

Exemplul  7 Vom crea o listă de tip TreeSet care va conține elemente de tip Elev sortate descrescător după notă. Fără elemente duplicat.
import java.util.*;
class Elev {
private String nume;
private int nota;
public Elev(String nume, int nota) {
this.nume = nume;
this.nota = nota;
}
public String getNume() {
return nume;
}
public int getNota() {
return nota;
}
@Override
public String toString() {
return nume + " - Nota: " + nota;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Elev)) return false;
Elev elev = (Elev) o;
return nota == elev.nota && nume.equals(elev.nume);
}
@Override
public int hashCode() {
return nume.hashCode() + nota;
}}

public class Main {
public static void main(String[] args) {
Set<Elev> elevi = new TreeSet<>(Comparator.comparingInt(Elev::getNota).reversed());
elevi.add(new Elev("Ana", 9));
elevi.add(new Elev("Ion", 7));
elevi.add(new Elev("Maria", 10));
elevi.add(new Elev("Bogdan", 8));
elevi.add(new Elev("Ana", 9));
System.out.println("Elevi sortati descrescător după nota:");
elevi.forEach(x -> System.out.println(x));
}
}
Pentru a efectua o sortare alfabetică după nume este suficient să folosim constructorul cu următorul parametru personalizat de sortare: 
Set<Elev> elevi = new TreeSet<>(Comparator.comparing(Elev::getNume));
Pentru o sortare pe două nivele putem folosi constructor de forma celui ce urmează care va efectua o sortare alfabetică după nume și descrescătoare după notă:  
        Comparator<Elev> comparator = Comparator.comparing(Elev::getNume.thenComparing (Comparator.comparingInt(Elev::getNota).reversed());
Pentru o sortare pe trei nivele amputea utiliza constructorul personalizat:
Comparator<Elev> comparator = Comparator.comparing(Elev::getNume.thenComparing(Elev::getPrenume.thenComparing (Comparator.comparingInt(Elev::getNota).reversed());

Pentru a evidenția diferențele și avantajele fiecărei implementări a interfeței Set, voi atașa cestei teme tabelul comparativ între HashSet, LinkedHashSet și TreeSet, care este concentrat pe ordonare, performanță și modul de gestionare a elementelor duplicat
Caracteristică HashSet LinkedHashSet TreeSet
Interfață implementată Set Set NavigableSet (extinde SortedSet și Set)
Ordinea elementelor Nu este garantată Păstrează ordinea de inserare Elementele sunt sortate natural (alfabetic/numeric) sau după Comparator
Permite duplicate Nu Nu Nu
Permite elemente null Da (doar un singur null) Da (doar un singur null) Nu (aruncă NullPointerException)
Complexitate adăugare/ștergere/căutare O(1) în medie O(1) în medie O(log n)
Structură internă Tabel hash Tabel hash + listă dublu înlănțuită Arbore binar de căutare
Avantaje Foarte rapid Rapid + ordinea elementelor păstrată Elemente sortate, navigare facilă
Dezavantaje Ordinea nu este păstrată Consum mai mare de memorie decât HashSet Operații mai lente decât HashSet/LinkedHashSet
Când se folosește Când ordinea nu contează și avem nevoie de viteză Când vrem ordinea de inserare și unicitate Când avem nevoie de elemente sortate automat

În concluzie, alegerea corectă a unei implementări a interfeței
Set depinde de cerințele aplicației: dacă este nevoie de viteză, se va utiliza HashSet, dacă se dorește păstrarea ordinii de inserare se va utiliza LinkedHashSet, iar dacă este necesară sortarea automată a elementelor – TreeSet. Toate aceste colecții asigură unicitatea elementelor, însă diferă prin modul de organizare internă și performanță.
Vă încurajez să testați fiecare tip de set prin exemple proprii și să observați diferențele în comportament – aceasta este cea mai bună metodă de învățare în programare, testați, modificați, ajustați, corectați erori de compilare și execuție.

Vă doresc o zi deosebit de frumoasă!
❤️