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
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:
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:
import java.util.HashSet;
import java.util.Set;
public class Main{
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.
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
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 {
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;
}@Overridepublic String toString() {
return nume + " - Nota: " + nota;
}
}
public class Main {
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().
import java.util.Set;
class Elev {
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;
}@Overridepublic String toString() {
return nume + " - Nota: " + nota;
}@Overridepublic 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);
}@Overridepublic int hashCode() {
return nume.hashCode() + nota;
}
}
public class Main {
public static void main(String[] args) {
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));
}}
public class Main {
Î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+" ");
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+" ");
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;
}@Overridepublic String toString() {
return nume + " - Nota: " + nota;
}@Overridepublic 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);
}@Overridepublic int hashCode() {
return nume.hashCode() + nota;
}}
public class Main {
public static void main(String[] args) {
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 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ă: }
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));
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());
| 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ă!
❤️
Niciun comentariu:
Trimiteți un comentariu
Vă mulțumesc pentru feedback!