joi, 26 februarie 2026

Expresiile lambda

O expresie lambda este un bloc scurt de cod care primește parametri și returnează o valoare. Mai este numită funcție anonimă (fără nume) și permite scrierea unui cod mai scurt și mai clar, în special atunci când lucrăm cu colecții sau interfețe funcționale.

O interfață funcțională în Java este o interfață care conține exact o singură metodă abstractă (SAM - Single Abstract Method), servind ca bază pentru expresiile lambda și transmiterea acțiunilor ca parametru.

Sintaxa expresiilor lambda:
(parametri) -> expresie
sau
(parametri) -> { instructiuni }
Exemple:
(x) -> x * 2
(x, y) -> x + y
() -> System.out.println("Salut!")


După cum se observă în sintaxa expresiei lambda, aceasta are trei părți principale:
1. Parametrii - reprezintă datele de intrare. În funcție de prezența sau absența parametrilor expresiile pot fi:
a. fără parametri: ()
b. cu un parametru: (x)
c. mai mulți: (x, y)
2. Operatorul lambda -> separă parametri de corpul expresiei și se citește ca ”produce”
3. Corpul expresiei - spune ce face efectiv expresia lambda, adică reprezintă funcționalitatea sau acțiunea expresiei. La fel ca instrucțiunile aceasta poate fi:
a. Simplă: x -> x * 2
b. Compusă: 
x -> {
System.out.println(x);
return x * 2;
}
O expresie lambda nu funcționează singură. Ea trebuie ”legată” de un context unde există o interfață funcțională. De aceea acestea sunt utilizate, în principal, împreună cu interfețe funcționale (precum Runnable, Comparator, Predicate sau una definită de programator) și la lucrul cu colecții de date. 

În această lecție vom vedea cum expresiile lambda ne ajută să lucrăm mai rapid și mai ușor cu listele de date. 

Exemplul 1 Prelucrarea colecțiilor pentru efectuarea operațiilor de filtrare, sortare sau iterarea elementelor colecțiilor.
import java.util.ArrayList;
public class Main
{
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(7);
numbers.add(4);
numbers.add(2);
numbers.add(7);

System.out.println("Afisare elemente folosind lambda expresii");
numbers.forEach( n -> System.out.print(n+" "));
System.out.println("\n-----------------------------------------");

System.out.println("Afisare elementelor a patrat folosind lambda expresii");
numbers.forEach( n -> System.out.print(n*n+" "));
System.out.println("\n-----------------------------------------");

System.out.println("Afisare dacă elementul este > sau < decit 5:");
numbers.forEach(n -> {
if(n < 5)
System.out.println(n + " este mai mic");
else
System.out.println(n + " este mai mare");
});
System.out.println("\n-----------------------------------------");

System.out.println("Afisare elementelor pare folosind lambda expresii");
numbers.forEach( n -> {if (n%2 == 0) System.out.print(n+" ");});
System.out.println("\n-----------------------------------------");

System.out.println("Sortare crescătoare folosind lambda expresii");
numbers.sort((a, b) -> a - b);
numbers.forEach( n -> System.out.print(n+" "));
System.out.println("\n-----------------------------------------");

System.out.println("Sortare descrescătoare folosind lambda expresii");
numbers.sort((a, b) -> b-a);
numbers.forEach( n -> System.out.print(n+" "));
System.out.println("\n-----------------------------------------");

System.out.println("Eliminare elemente duplicat");
numbers.removeIf(n -> numbers.indexOf(n) != numbers.lastIndexOf(n));
numbers.forEach(n -> System.out.print(n + " "));
System.out.println("\n-----------------------------------------");

System.out.println("Sortare crescătoare (text) folosind lambda expresii");
ArrayList<String> persoane = new ArrayList<String>();
persoane.add("Gabriel");
persoane.add("Ion");
persoane.add("Chiril");
persoane.sort((a, b) -> a.compareTo(b));
persoane.forEach(x -> System.out.print(x+" "));
System.out.println("\n-----------------------------------------");

System.out.println("Sortare descrescătoare (text) folosind lambda expresii");
persoane.sort((a, b) -> b.compareTo(a));
persoane.forEach(x -> System.out.print(x+" "));
System.out.println("\n-----------------------------------------\n");
}}



Exemplul 2 Prelucrarea colecțiilor pentru efectuarea deferitor operații de prelucrare a listelor care au ca tip o clasă definită de utilizator
import java.util.ArrayList;
class Elev {
String prenume;
String nume;
public Elev(String prenume, String nume) {
this.prenume = prenume;
this.nume = nume;
}

public String toString() {
return prenume + " " + nume;
}}
public class Main {
public static void main(String[] args) {
ArrayList<Elev> elevi = new ArrayList<>();
elevi.add(new Elev("Ion", "Popescu"));
elevi.add(new Elev("Gabriel", "Ionescu"));
elevi.add(new Elev("Chiril", "Moraru"));
elevi.add(new Elev("Ion", "Albu"));

System.out.println("-----------------------------------------");
System.out.println("Lista inițială:");
elevi.forEach(e -> System.out.println(e));

System.out.println("-----------------------------------------");
System.out.println("Elevi sortati crescator dupa prenume:");
elevi.sort((e1, e2) -> e1.prenume.compareTo(e2.prenume));
elevi.forEach(e -> System.out.println(e));
System.out.println("-----------------------------------------");

System.out.println("Elevi sortati crescator dupa nume:");
elevi.sort((e1, e2) -> e1.nume.compareTo(e2.nume));
elevi.forEach(e -> System.out.println(e));
System.out.println("-----------------------------------------");

System.out.println("Elevi sortati crescator dupa prenume si nume");
elevi.sort((e1, e2) -> {
int rezultat = e1.prenume.compareTo(e2.prenume);
if (rezultat == 0) {
return e1.nume.compareTo(e2.nume); }
return rezultat;
});
elevi.forEach(e -> System.out.println(e));
System.out.println("-----------------------------------------");

System.out.println("Modificare date elev Popescu Ion in Andrei varianta I:");
elevi.replaceAll(e -> {     
//înocuim înterg obiectul
if(e.prenume.equals("Ion") && e.nume.equals("Popescu"))
return new Elev("Andrei", "Popescu");
return e;
});
elevi.forEach(e -> System.out.println(e));
System.out.println("-----------------------------------------");

System.out.println("Modificare date elev Popescu Andrei in Ion varianta II:");
elevi.forEach(e -> {
if(e.prenume.equals("Andrei") && e.nume.equals("Popescu")) {
e.prenume = "Ion";   
//înlocuim doar câmpul
}
});
elevi.forEach(e -> System.out.println(e));
System.out.println("-----------------------------------------");

System.out.println("Eliminarea elevului Popescu Ion:");
elevi.removeIf(e -> e.prenume.equals("Ion") && e.nume.equals("Popescu"));
elevi.forEach(e -> System.out.println(e));
System.out.println("-----------------------------------------");

System.out.println("Eliminarea elevilor al căror nume incepe cu litera I:");
elevi.removeIf(e -> e.nume.startsWith("i")||e.nume.startsWith("I"));
elevi.forEach(e -> System.out.println(e));
System.out.println("-----------------------------------------");
}}


În compunearea și aplicarea expresiilor lambda vom ține cont de următoarele reguli:
- Dacă expresia lambda nu primește parametri, parantezele sunt obligatorii: () -> 20
- Dacă expresia lambda are un singur parametru, parantezele rotunde pot fi omise: e -> System.out.println(e)
- Dacă expresia lambda are mai mulți parametri atunci parantezele sunt obligatorii: (a, b) -> a.compareTo(b)
- Dacă în interiorul expresiei lambda este o singură instrucțiune atunci cuvântul cheie return nu este obligatoriu de utilizat, la fel ca și acoladele: (e1, e2) -> e1.nume.compareTo(e2.nume)
- Dacă în interiorul expresiei lambda sunt mai multe instrucțiuni atunci cuvântul cheie return este obligatoriu de utilizat, la fel ca și acoladele:
e -> {
if(e.prenume.equals("Ion") && e.nume.equals("Popescu"))
return new Elev("Andrei", "Popescu");
return e;
});
- Lambda poate fi folosită doar acolo unde se așteaptă o interfață funcțională
- Tipul parametrilor poate fi dedus de compilator sau declarat explicit.
- Folosește lambda pentru cod scurt și clar, dacă blocul de cod e lung, poate fi mai lizibil să scrii o metodă separată.
- Evită efectele secundare în lambda (modificarea variabilelor globale), pentru claritate.

Parcurgeți următorul algoritm pentru a decide aveți nevoie de expresii lambda sau nu în contextul proiectului dvs:
De unde știm ce interfață are nevoie expresia lambda ca să funcționeze corect? 
Nu lambda decide tipul de interfață, ci metoda pe care o folosești. De exemplu: 
  • metoda forEach() are nevoie de o acțiune, ea nu returnează valori, aici ar fi ideea că pentru fiecare element din listă fă ceva, o acțiune, de exemplu afișează-l. Expresia lambda în acest caz operează cu interfața Consumer
  • metoda sort() așteaptă o regulă de comparare pentru a aranja anumite elemente într-o anumită ordine. Expresia lambda aici operează cu interfața Comparator
  • metoda removeIf() are nevoie de o condiție pentru a efectua ștergerea și a filtra datele. Aici expresia operează cu interfața Predicate
  • metoda replaceAll() are neoie de o regulă de transformare pentru a înlocui fiecare element din listă cu o versiune modificată a acestuia. Expresia lambda aici operează cu interfața UnaryOperator.  
Putem afla ce interfață acceptă o metodă consultând documentația oficială Java. Aceasta prezintă metodele claselor și, în sintaxa fiecărei metode, interfața corespunzătoare pe care o putem folosi pentru a compune expresia lambda. De exemplu, în documentația clasei ArrayList, metoda removeIf() indică faptul că se poate folosi o expresie lambda care operează cu interfața Predicate:
Este important să reținem că interfața nu știe ce operație va fi aplicată, ea definește doar semnătura metodei, adică tipul de funcție permis, în timp ce lambda specifică exact ce face funcția.

Notă!
Limbajul Java are mai multe interfețe funcționale standard, dar majoritatea sunt limitate la un singur parametru (Function, Consumer, Predicate,ș.a) sau doi parametri (BiFunction, BiConsumer, BiPredicate,ș.a). 
Ca soluție pentru crearea unei expresiilor lambda cu trei și mai mulți parametri se propune:
1. Definirea unei interfețe funcționale proprii.
2. Ambalarea parametrilor într-un obiect și folosirea lambda cu un singur parametru care conține toate valorile.

Dar ...... despre asta vom discuta într-o altă postare.

Dacă această temă v-a trezit un interes mai deosebit vă provoc să studiați amănunțit documentația oficială Java la acest subiect: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#syntax

În contextul lecției curente, dacă ai un exemplu de expresie lambda care se potrivește cu exemplele discutate, te încurajez să-l adaugi în comentarii!

Să aveți o zi deosebită!
❤️

miercuri, 25 februarie 2026

Clasa ArrayList. Crearea listelor dinamice de obiecte

După ce am învățat să lucrăm cu vectori de obiecte, este momentul să facem un pas mai departe. Până acum am creat structuri de tipul:  

Elev[] elevi = new Elev[30];

Unde
Elev[] înseamnă un vector care poate stoca obiecte de tip Elev;
30 reprezintă numărul maxim de elemente;
fiecare poziție din vector reține un obiect.

Această metodă este corectă și foarte importantă pentru a înțelege cum funcționează memoria și organizarea datelor. 
Dar în aplicarea în practică, uneori această metodă este ineficientă. 
De ce?  Pentru că așa tip de vector are o dimensiune statică, în sens că dimensiunea acestuia este fixă, iar în cazul exemplului de mai sus nu vom putea adăuga al 31-lea elev, ba mai mult dacă inserăm în listă doar 10 elevi, restul pozițiilor rămân neutilizate. Ștergerea unui element necesită mutarea manuală a celorlalte.

În aplicațiile reale, de cele mai multe ori nu știm din start câte obiecte vom avea. Nu avem cum să cunoaștem câte persoane se vor înscrie la un concurs, câte persoane își vor crea conturi pe platforma educațională a instituției sau câți abiturienți vor depune dosarul pentru a participa la concursul de admitere. Respectiv, este nevoie de o structură dinamică dimensiunea căreia să fie ajustabilă.

Una din soluțiile care le oferă limbajul Java este utilizarea clasei ArrayList din pachetul java.util.  
Un ArrayList este un vector dinamic. Aceasta înseamnă că dimensiunea se modifică automat, avem posibilitatea de a adăuga elemente fără limită fixă, iar ștergerea elementelor este simplificată. 

După cum cunoaștem ca să creăm listă dinamică este necesar de instanțiat clasa ArrayList , deci trebuie să îi cunoaștem constructorii: 
ArrayList() Construiește o listă fără nici un element având capacitatea inițială de 10 elemente. Dacă se încearcă adăugarea a mai mult de 10 elemente, dimensiunea listei se va modifica automat.
ArrayList(Collection c) Construiește o listă ce conține elementele conținute de colecția primită ca argument. Capacitatea inițială este cu 10 procente mai mare decât numărul de elemente din colecția primită ca argument
ArrayList(int intialCapacity) Construiește o listă vidă cu o capacitate specificată de parametrul intialCapacity

Deci pentru a crea un ArrayList respectăm sintaxa: 
ArrayList<Elev> listaElevi = new ArrayList<>();

După cum observați dimensiunea listei nu o mai specificăm, așa cum aceasta crește automat. 

Pentru a putea opera cu elementele din listă vom trebuie să cunoaștem metodele clasei ArrayList și parametrii acestora:
 
1. add(E e)- adaugă la sfârșitul listei obiectul e
ArrayList<String> lista = new ArrayList<>();
lista.add("Ana"); // adaugă "Ana" în listă
lista.add("Ion"); // adaugă "Ion" în listă

2. add(int index, E e) - adaugă obiectul e în listă începând cu poziția index, mutând elementele de după el.
lista.add(1, "Maria"); // inserează "Maria" pe poziția 1

3. get(int index) - returnează elementul de la poziția indicată prin parametrul index
String nume = lista.get(0); // returnează "Ana"

4. set (int index, E e)- Înlocuiește elementul de pe poziția index cu noul element, sau obiect e
lista.set(0, "Andreea"); // înlocuiește "Ana" cu "Andreea" 

5. remove(int index)- șterge elementul de la poziția indicată și mută elementele următoare.
lista.remove(1); // șterge elementul de pe poziția 1 ("Maria")

6. remove(Object o) - șterge prima apariție a obiectului o din listă.
lista.remove("Andreea"); // șterge "Andreea" din listă

7.
size()- returnează numărul de elemente din listă.
int lungime = lista.size(); // returnează 0 dacă lista este goală

8. isEmpty()- returnează true dacă lista este goală.
if (lista.isEmpty()) {
System.out.println("Lista este goală");}

9. contains(Object o) - verifică dacă lista conține elementul dat.
boolean exista = lista.contains("Ion"); // returnează true dacă "Ion" este în listă

10. clear()- șterge toate elementele din listă.
lista.clear(); // lista devine goală

Crearea unei liste de tip ArrayList care să conțină direct tipuri primitive precum int sau double nu este posibilă, așa cum metodele clasei ArrayList operează cu tip de date referință. 

Totuși, încă de la începutul cursului am învățat că fiecărui tip primitiv din Java îi corespunde o clasă de tip Wrapper.
De exemplu:
tipului int îi corespunde clasa Integer
tipului double îi corespunde clasa Double
tipului char îi corespunde clasa Character
tipului boolean îi corespunde clasa Boolean

Astfel, dacă dorim o listă de numere întregi, vom scrie:
ArrayList<Integer> numere = new ArrayList<>();
numere.add(10);
numere.add(25);

Observați că folosim Integer, nu int.
Java face automat conversia dintre tipul primitiv și obiectul corespunzător. Acest mecanism se numește autoboxing (transformarea automată a unui tip primitiv într-un obiect Wrapper) și unboxing (transformarea inversă).

În exemplele de mai sus, elementele stocate în ArrayList sunt de tip String, pentru a simplifica înțelegerea. În practică însă, lista va conține, de cele mai multe ori, obiecte ale claselor create de programator, cum ar fi Client, Produs, Comandă sau alte tipuri definite în aplicație.

Să vedem în următorul exemplu cum este posibil acest lucru. 
Fie ca avem clasa Elev cu următorul cod sursă:
 
class Elev{
private String nume;
private int grupa;
 
public Elev(String nume, int grupa) {
this.nume = nume;
this.grupa = grupa;
}
public String getNume() {
return nume; }
 
public void setNume(String nume) {
this.nume = nume;
}
 
public int getGrupa() { return grupa; }
 
public void setGrupa(int grupa) {
this.grupa = grupa;
}} 

Următorul exemplu va crea o listă de elevi și va parcurge elementele acesteia folosind instrucțiunea for clasică cu index:

import java.util.*;
class TestCollection1{
    public static void main(String args[]){
        ArrayList<Elev> lista=new ArrayList<Elev>();
        lista.add(new Elev("Ana", 1001));
        lista.add(new Elev("Maria", 1002));
        lista.add(new Elev("Ion", 1001));
        lista.add(new Elev("Mihai", 1002));
        for (int i = 0; i < lista.size(); i++) {
            Elev element = lista.get(i);
            System.out.println(element.getNume()+
                    " "+element.getGrupa());}
    }}

O altă modalitate de a parcurge lista este instrucțiunea for îmbunătățit, dar ... noi deja cunoaștem în ce context o folosim 👌

import java.util.*;
class TestCollection1{
    public static void main(String args[]){
         ArrayList<Elev> lista=new ArrayList<Elev>();
         lista.add(new Elev("Ana", 1001));
         lista.add(new Elev("Maria", 1002));
         lista.add(new Elev("Ion", 1001));
         lista.add(new Elev("Mihai", 1002));
         for (Elev element : lista) {
             System.out.println(element.getNume()+
             " "+element.getGrupa());}

    }}


Voi prezenta mai jos o metodă de parcurgere a listei destul de des folosită în proiecte reale: 
import java.util.*;
class TestCollection1{
    public static void main(String args[]){
        ArrayList<Elev> lista=new ArrayList<Elev>();
        lista.add(new Elev("Ana", 1001));
        lista.add(new Elev("Maria", 1002));
        lista.add(new Elev("Ion", 1001));
        lista.add(new Elev("Mihai", 1002));
        lista.forEach(element -> {
            System.out.println(element.getNume()+
                    " "+element.getGrupa());
        });
    }}

Metoda
forEach(element -> { ... }) din ultimul exemplu folosește o expresie lambda pentru parcurgerea listei.  Expresiile lambda sunt o funcționalitate în limbaj introdusă în Java8. Tradus din limbaj Java în limbaj omenesc ar însemna: ,,pentru fiecare element din listă, execută instrucțiunile din bloc”.

Sunt și alte metode de parcurgere și modificare a listelor de obiecte cum ar fi interfața Iterator, Interfața Enumeration, dar vom discuta despre ele într-o altă postare.

Succes!
❤️

luni, 23 februarie 2026

Expresii regulate. Validarea șirurilor de caractere

În Java, o expresie regulată, numită regex, este un model de verificare a unui text. Ea ne permite să controlăm dacă un șir respectă o anumită regulă sau nu. 
De exemplu putem valida dacă șirul introdus de utilizator este: email, număr de telefon, parolă, cod poștal, doar cifre, doar litere, etc

Pentru aceasta se folosesc clasele din pachetul java.util.regex
import java.util.regex.Pattern;
import java.util.regex.Matcher;
Dar un mod mai simplu de a valida un șire de caractere este metoda matches() a clasei String care are forma:
String.matches("regex");
"regex" este o expresie regulată, adică un model (regulă) după care verificăm textul, cu alte cuvinte anume acesta este șablonul după care validăm șirul de caractere. 
Pentru a construi șablonul trebuie să cunoaștem simbolurile cheie: 

Vom examina în continuare mai multe exemple practice. 

Exemplul următor va valida dacă șirul de caractere conține doar cifre. Expresia regulată în acest caz va conține \\d,  adică doar cifre, și + ceea ce înseamnă una sau  mai multe apariții, deci verificăm dacă în șirul de caractere apare una sau mai multe cifre: 
class Testare{
public static void main(String [] args){
String text = "12345";
if (text.matches("\\d+")) {
System.out.println("Codul este valid!");}
else
System.out.println("Codul trebuie să conțină doar cifre!");
}}

O astfel de verificare este frecvent utilizată în aplicații reale: validarea unui cod numeric, a unui ID, a unui PIN sau a unui număr de ordine.

De exemplu cunoaștem că un code PIN are 4 cifre, deci expresia care va valida dacă șirul de caractere este un cod corecte va conține {} și acesta este: 
class Testare{
public static void main(String [] args){
String pin = "2255";
if (pin.matches("\\d{4}")) {
System.out.println("Codul pin valid");
}
}}

Exemplul următor va valida dacă șirul de caractere conține doar litere. Expresia regulată în acest caz va conține [a-zA-Z],  adică doar litere mari și mici, și semnul + ceea ce înseamnă una sau  mai multe apariții, deci verificăm dacă în șirul de caractere apare una sau mai multe litere, fie litere mari, fie litere mici: 
class Testare{
public static void main(String [] args){
String text = "ProgramareJava";
if (text.matches("[a-zA-Z]+")) {
System.out.println("Valid - doar litere");
}
}}

Următorul exemplu verifică dacă șirul de caractere va conține cel puțin o literă acesta nu va promova validarea: 
class Testare{
 public static void main(String [] args){
     String text = "1F2345";
     if (text.matches(".*[a-zA-Z].*")) {
         System.out.println("Codul este valid!");}
     else System.out.println("Codul trebuie să conțină cel puțin o literă!");
 }}

În cazul condițiilor combinate este necesar de utilizat simbolurile:
^  - începutul șirului
$  - sfârșitul șirului

De exemplu, vom verifica dacă șirul conține litere și cifre: 
class Testare{
public static void main(String [] args){
String text = "Java2026";
if (text.matches("^[a-zA-Z0-9]+$")) {
System.out.println("Valid - doar litere si cifre");
}
else {
System.out.println("Invalid");
}
}}

De asemenea, în condițiile combinate se folosește (?=…), numit lookahead, care verifică o condiție fără să „consume” textul, adică se uită dacă regula este respectată, dar nu modifică șirul și nu utilizează caracterele verificate, nu-i modifică dimensiunea, astfel încât restul expresiei să poată continua să analizeze întreg șirul.

Următoarele două exemple evidențiază utilitatea lookahead-ului în condiții combinate, dorim să validăm dacă parola conține cel puțin o cifră și minim 6 caractere : 

Exemplu fără lookahead
class Testare{
public static void main(String [] args){
String parola = "abc123";
// Regex fără lookahead
// Caută să înceapă cu o cifră, apoi 5 sau mai multe caractere
if (parola.matches("\\d.{6,}")) {
System.out.println("Parola este validă");
}
else { System.out.println("Parola NEVALIDĂ");}
}}


Deși parola conține cel puțin o cifră și minim 6 caractere, aceasta nu trece testul de validare. Este din motiv că regex fără lookahead caută să înceapă cu o cifră, în caz contrar rezultatul este false.

Exemplu cu lookahead
class Testare{
public static void main(String [] args){
String parola = "abc123";
if (parola.matches("^(?=.*\\d).{6,}$")) {
System.out.println("Parola este validă");
}
else { System.out.println("Parola NEVALIDĂ");}
}}

Exemplu în care vom verifica dacă utilizatorul indică un email corect: 

class Testare{
public static void main(String [] args){
String email = "test@gmail.com";
if (email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
System.out.println("Email valid");
}
else { System.out.println("Email nu este valid");}
}}

Exemple de expresii regex și destinația acestora:

.matches("^[a-zA-Z0-9_]+$") - verifică dacă expresia conține litere, cifre și underscore, util pentru validare username


.matches("^\\d{6}$") - verifică dacă șase cifre, util pentru verificarea unui cod poștal

.matches("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}$") - verifică dacă șirul de caractere conține cifre, litere mici, litere mari, util pentru verificarea unei parole mai complexe

.matches("^[A-Za-z].*$") - verifică dacă șirul de caractere începe cu o litera

.matches(".*\\d$") - verifică dacă șirul de caractere finisează cu o cifră

.matches("^\\d+(\\.\\d+)?$") - verifică dacă șirul de caractere  este un număr zecimal. Va accepta de exemplu 10, la fel și 10.5. Nu va accepta litere

.matches("^[A-Za-z][A-Za-z0-9_]*$") - verifică dacă șirul de caractere  începe cu o literă după care urmează litere, cifre sau semnul underscore

Expresiile regulate sunt foarte utile în programare, oferind o metodă rapidă și precisă pentru verificarea și manipularea șirurilor de caractere, astfel asigurând securitatea datelor cu care lucrează aplicația. 

Îți dorești un anumit conținut să apară în această postare? 
Vezi meniul 👉 CONTACTE 
Aștept un e-mail 💬

Succes!
❤️

luni, 16 februarie 2026

Set de probleme. Clasa StringBuilder. Clasa StringBuffer


1. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Salut" la care adăugați folosind append() numele dvs și un semn de punctuație. La final, afișați rezultatul pentru ambele obiecte. Rezultat așteptat: "Salut, [numele tău]!"

2. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Salut dragă lume", ștergeți cuvântul "dragă " folosind delete(). Afișați rezultatul pentru ambele clase. Rezultat așteptat: "Salut lume"

3. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Salut lume!". Folosiți deleteCharAt() pentru a elimina semnul de exclamare, aplicând metoda atât pe StringBuffer, cât și pe StringBuilder. Rezultat așteptat: "Salut lume"

4. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Buna ziua". Înlocuiți "ziua" cu "seara" folosind replace(). Afișați rezultatul pentru ambele clase. Rezultat așteptat: "Buna seara"

5. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Java". Folosiți reverse() pentru a inversa șirul, aplicând metoda atât pe StringBuffer, cât și pe StringBuilder. Rezultat așteptat: "avaJ"

6. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Salut lume. Eu programez în Java". Afișați caracterul aflat la poziția pe o anumită poziție folosind metoda charAt() pentru ambele clase.

7. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "salut lume". Folosiți metoda setCharAt() pentru a schimba prima literă în majusculă, atât pentru StringBuffer, cât și pentru StringBuilder. Rezultat așteptat: "Salut lume"

8. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Programare Java". Extrageți cu ajutorul metodei substring() doar cuvântul "Java" pentru ambele clase.

9. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder în baza unui text citit de la tastatură. Afișați la ecran, folosind metoda length() câte caractere are textul citit.

10. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Salut". Afișați capacitatea inițială. Apoi adăugați mai multe caractere. Afișați capacitatea după modificarea textului. Observați cum se schimbă capacitatea pentru ambele clase.

11. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Invat programare". Introduceți cuvântul „Java ” după cuvântul „Invat ” folosind metoda insert(). Adăugați punct la final folosind metoda append(). Afișați rezultatul. Încercați să utilizați apelul în lanț al metodelor.

12. Un elev a stocat o listă de teme pentru acasă într-un șir de caractere:

tema1: matematica, tema2: informatica, tema3: fizica

Scrieți un program în care să utilizați StringBuilder sau StringBuffer pentru a obține la afișare:

TEMA1: MATEMATICA | TEMA2: INFORMATICA | TEMA3: FIZICA

Pentru aceasta:
- Transformă fiecare temă în majuscule.
- Înlocuiește virgulele cu simbolul |.
- Afișează rezultatul final.

13. Un magazin online a stocat titlurile comenzilor într-un șir de caractere:

comanda1: telefon, comanda2: laptop, comanda3: tableta

Scrieți un program în care să utilizați fie StringBuilder, sau StringBuffer pentru a obține la afișare:

Comanda3: TABLETA | Comanda2: LAPTOP | Comanda1: TELEFON

Pentru aceasta:
- Eliminați spațiile inutile de la început și sfârșit folosind trim().
- Inversați ordinea comenzilor folosind reverse().
- Transformați numele produselor în majuscule.
- Înlocuiți virgulele cu simbolul |.


Succes!
💓

duminică, 15 februarie 2026

Șiruri de caractere. Clasa StringBuilder. Clasa StringBuffer

În postările anterioare am discutat despre șiruri de caractere și clasa String, știm deja că această clasă este imutabilă, adică orice modificare creează un obiect nou în memorie. Acest lucru poate deveni ineficient atunci când lucrăm cu volume mari de text sau efectuăm modificări repetate asupra șirurilor de caractere.

Astăzi discutăm despre două clase care rezolvă această problemă: StringBuilder și StringBuffer
Deci și aceste clase Java permit prelucrarea șirurilor de caractere. Să vedem care totuși diferența între ele: 
1. String - este imutabil. Fiecare modificare creează un obiect nou. Este mai lent în concatenări repetate.
2. StringBuilder - este mutabil. Fiecare modificare efectuată asupra șirului modifică obiectul curent. Este mai rapid și mai recomandat în majoritatea aplicațiilor.
3. StringBuffer - este mutabil. Metodele sale sunt sincronizate și este recomandat pentru aplicații multifir. Este mai lent decât StringBuilder.

Deci, clasa String pune accent pe siguranță prin imutabilitate. Clasa StringBuilder pune accent pe performanță, iar clasa StringBuffer pune accent pe siguranță în aplicații concurente. 
Reeșind din contextul problemei de rezolvat, fiecare programator decide care clasă este potrivită de utilizat.

Vom analiza metodele claselor StringBuilder și StringBuffer

a) append(textNou) - adaugă un text la sfârșitul unui șir de caractere.

Exemplu: 
public class StringBufferReader {
public static void main(String[] args) {
String textInitial = "Salut!";
StringBuffer buffer = new StringBuffer();
buffer.append(textInitial);
buffer.append(" Cum ești?");
System.out.println("StringBuffer: " + buffer);

StringBuilder builder = new StringBuilder();
builder.append(textInitial);
builder.append(" Sper că bine!");
System.out.println("StringBuilder: " + builder);
}}

b) insert(index, text) - permite să adaugi text într-o anumită poziție, nu doar la sfârșit.
Exemplu:
public class StringBufferReader {
public static void main(String[] args) {
String textInitial = "Salut lume!";

StringBuffer buffer = new StringBuffer(textInitial);
buffer.insert(6, "frumoasă ");
System.out.println("StringBuffer: " + buffer);

StringBuilder builder = new StringBuilder(textInitial);
builder.insert(6, "dragă ");
System.out.println("StringBuilder: " + builder);
}}

c) delete(start, end) - permite ștergerea unei secvențe de caractere dintre două poziții, începând de la start până la end - 1.
Exemplu:
public class StringBufferReader {
public static void main(String[] args) {
String textInitial = "Salut frumoasă lume!";

StringBuffer buffer = new StringBuffer(textInitial);
buffer.delete(6, 15); // șterge caracterele de la index 6 până la 14
System.out.println("StringBuffer după delete: " + buffer);

StringBuilder builder = new StringBuilder(textInitial);
builder.delete(5, 14); // șterge caracterele de la index 5 până la 13
System.out.println("StringBuilder după delete: " + builder);
}}

d) deleteCharAt(int index) - șterge un singur caracter aflat la poziția indicată.
Exemplu:
public class StringBufferReader {
public static void main(String[] args) {
String textInitial = "Salut lume!";

StringBuffer buffer = new StringBuffer(textInitial);
buffer.deleteCharAt(5); // șterge caracterul de la index 5 (spațiul)
System.out.println("StringBuffer după deleteCharAt: " + buffer.toString());

StringBuilder builder = new StringBuilder(textInitial);
builder.deleteCharAt(0); // șterge primul caracter 'S'
System.out.println("StringBuilder după deleteCharAt: " + builder.toString());
}}

e) replace(start, end, textNou) - adaugă text la sfârșitul unui șir de caractere.

Exemplu:
public class StringBufferReader {
public static void main(String[] args) {
String textInitial = "Salut lume!";

StringBuffer buffer = new StringBuffer(textInitial);
buffer.replace(6, 10, "univers"); // înlocuiește "lume" cu "univers"
System.out.println("StringBuffer după replace: " + buffer.toString());

StringBuilder builder = new StringBuilder(textInitial);
builder.replace(0, 5, "Bună"); // înlocuiește "Salut" cu "Bună"
System.out.println("StringBuilder după replace: " + builder.toString());
} }

f) reverse() inversează toate caracterele din obiectul respectiv.
Exemplu: 
public class StringBufferReader  {
public static void main(String[] args) {

StringBuffer buffer = new StringBuffer("Salut lume");
buffer.reverse();
System.out.println("StringBuffer după reverse: " + buffer.toString());

StringBuilder builder = new StringBuilder("Java este frumos");
builder.reverse();
System.out.println("StringBuilder după reverse: " + builder.toString());
}}

g) length() returnează numărul de caractere din obiectul respectiv. 
Exemplu: 
public class StringBufferReader{
public static void main(String[] args) {

StringBuffer buffer = new StringBuffer("Salut lume!");
int lungimeBuffer = buffer.length();
System.out.println("Lungimea StringBuffer: " + lungimeBuffer);


StringBuilder builder = new StringBuilder("Java este frumos");
int lungimeBuilder = builder.length();
System.out.println("Lungimea StringBuilder: " + lungimeBuilder);
}}

h) capacity() - arată cât spațiu intern este alocat pentru caractere, adică cât
text poate conține fără să fie nevoie să realoce memorie. Nu este lungimea textului,
spațiul intern disponibil. Dacă capacitatea este depășită, obiectul mărește automat
spațiul intern pentru a stoca caracterele suplimentare, fără a pierde date.

Exemplu: 
public class StringBufferReader{
public static void main(String[] args) {

StringBuffer buffer = new StringBuffer("Salut lume!");
System.out.println("Capacity StringBuffer: " + buffer.capacity());

StringBuilder builder = new StringBuilder("Java este frumos");
System.out.println("Capacity StringBuilder: " + builder.capacity());
}}
j) charAt(int index) - returnează caracterul aflat la poziția specificată în șir.
Exemplu:
public class StringBufferReader {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder("Salut lume!");

char c = builder.charAt(0); // primul caracter
System.out.println("Caracterul de la index 0: " + c);

char d = builder.charAt(6); // caracterul de la index 6
System.out.println("Caracterul de la index 6: " + d);
}}
k) setCharAt(int index, char c) - permite să înlocuiți caracterul de la o poziție
specifică cu un alt caracter.
Exemplu:

public class StringBufferReader{
public static void main(String[] args) {
StringBuffer buffer = new StringBuffer("Salut lume!");
buffer.setCharAt(0, 's'); // schimbăm 'S' în 's'
buffer.setCharAt(6, 'L'); // schimbăm 'l' în 'L'
System.out.println("StringBuffer după setCharAt: " + buffer);

StringBuilder builder = new StringBuilder("Java este frumos");
builder.setCharAt(0, 'j'); // schimbăm 'J' în 'j'
builder.setCharAt(5, 'E'); // schimbăm 'e' în 'E'
System.out.println("StringBuilder după setCharAt: " + builder);
}}


k)
substring(int start) și substring(int start, int end) - permite
să extragi o porțiune de text din obiectul respectiv, fără să îl modifici.
Exemplu:
public class StringBufferReader {
public static void main(String[] args) {

StringBuffer buffer = new StringBuffer("Salut lume!");
String subBuffer = buffer.substring(6); // din index 6 până la final
String subBuffer2 = buffer.substring(0, 5); // din index 0 până la 4
System.out.println("StringBuffer substring 6: " + subBuffer);
System.out.println("StringBuffer substring 0-5: " + subBuffer2);

StringBuilder builder = new StringBuilder("Java este frumos");
String subBuilder = builder.substring(5); // din index 5 până la final
String subBuilder2 = builder.substring(0, 4); // din index 0 până la 3
System.out.println("StringBuilder substring 5: " + subBuilder);
System.out.println("StringBuilder substring 0-4: " + subBuilder2);
}}
l) toString() - returnează conținutul obiectului ca un String normal, permițând să îl folosești oriunde este nevoie de un șir de caractere.
Exemplu:
public class StringBufferReader {
public static void main(String[] args) {

StringBuffer buffer = new StringBuffer("Salut lume!");
String textBuffer = buffer.toString();
System.out.println("StringBuffer toString: " + textBuffer);

StringBuilder builder = new StringBuilder("Java este frumos");
String textBuilder = builder.toString();
System.out.println("StringBuilder toString: " + textBuilder);
}}


Nu pot să nu subliniez că, datorită caracterului lor mutabil, clasele StringBuilder și StringBuffer permit apelul în lanț al metodelor, astfel încât mai multe operații de manipulare a textului pot fi combinate într-un singur bloc de cod, obținând rapid și elegant rezultatul dorit.

Exemplu:
public class StringBufferReader {
public static void main(String[] args) {

StringBuffer buffer = new StringBuffer("Buna");
buffer.append(" dragi") // adaugă text
.insert(5, " ziua ") // inserează " ziua" la poziția 5
.append(" elevi!") // adaugă la final
.replace(0, 4, "Salut"); // înlocuiește "Buna" cu "Salut"

System.out.println("StringBuffer: " + buffer);

StringBuilder builder = new StringBuilder("Buna");
builder.append(" dimineața") // adaugă text
.insert(4, " tuturor") // inserează " tuturor" la poziția 4
.append("!") // adaugă semn de punctuație
.replace(0, 4, "Salut"); // înlocuiește "Buna" cu "Salut"

System.out.println("StringBuilder: " + builder);
}
}

În același timp, clasa String nu permite apelul în lanț în același mod ca StringBuilder sau StringBuffer, și iată de ce:
a) Obiectele
String sunt imutabile.
b) Orice metodă care modifică textul (ex:
replace, substring, concat) nu schimbă obiectul original, ci returnează un obiect String nou.

Deși poți scrie apeluri în lanț cu String, fiecare apel creează un nou obiect în memorie, ceea ce este mai puțin eficient comparativ cu StringBuilder sau StringBuffer.

Exemplu
public class StringBufferReader {
public static void main(String[] args) {
String text = "Salut lume";
text.replace("Salut", "Buna")
.toUpperCase()
.concat("!");
System.out.println(text); // Salut lume
}}

Observați că, deși am încercat să aplicăm metodele în lanț pe un obiect String, rezultatul afișat nu s-a schimbat, deoarece String este imutabil. În schimb, clasele StringBuilder și StringBuffer, fiind mutabile, permit modificarea directă a obiectului și suportă apelul în lanț al metodelor, oferind rezultate imediate și eficiente.

Vom reface exemplul de mai sus astfel încât, totuși să vedem cum este posibil apelul în lanț în cazul clasei String.

Exemplu:
public class StringBufferReader {
public static void main(String[] args) {
String text = "Salut lume";
String textNou = text.replace("Salut", "Buna")
.toUpperCase()
.concat("!");
System.out.println(textNou);
}
}


După cum observăm în acest exemplu, pentru a obține efectul dorit în cazul clasei String, trebuie să salvăm rezultatul fiecărei metode într-o variabilă nouă, deoarece obiectul original rămâne neschimbat.

Clasele StringBuilder și StringBuffer oferă funcții diverse pentru manipularea șirurilor de caractere, fiecare cu avantajele sale, iar pentru o înțelegere rapidă și clară, tabelul comparativ de mai jos sintetizează caracteristicele principale ale acestor două clase și ne vor ajuta să înțelegem care este clasa potrivită de prelucrare a șirurilor de caractere pentru o anumită situație:


Succes!
❤️