🗺️ Java Map Interface

Ghid complet cu exemple practice și explicații detaliate

📚 Ce este Map în Java?

Map este o interfață fundamentală în Java Collections Framework care stochează date sub formă de perechi cheie-valoare (key-value pairs). Spre deosebire de List sau Set, Map nu extinde interfața Collection, având propria ierarhie.

🔑 Chei Unice

Fiecare cheie poate apărea o singură dată în Map

🎯 Acces Rapid

Permite accesarea rapidă a valorilor folosind cheia

🔄 Valori Duplicate

Valorile pot fi duplicate, doar cheile sunt unice

📊 Structură Flexibilă

Ideal pentru mapări, dicționare și cache-uri

Declararea Interfeței Map

public interface Map<K, V> {
    // K - tipul cheilor
    // V - tipul valorilor
    
    V put(K key, V value);
    V get(Object key);
    V remove(Object key);
    boolean containsKey(Object key);
    boolean containsValue(Object value);
    int size();
    boolean isEmpty();
    void clear();
}

🔧 Tipuri de Map în Java

1. HashMap

HashMap este cea mai folosită implementare a interfeței Map. Folosește hash table pentru stocare.

  • ✅ Permite null pentru cheie și valori
  • ✅ Nu garantează ordinea elementelor
  • ✅ Performanță O(1) pentru operații de bază
  • ❌ Nu este sincronizat (not thread-safe)
import java.util.HashMap;

// Crearea unui HashMap
HashMap<String, Integer> varste = new HashMap<>();

// Adăugarea elementelor
varste.put("Ion", 25);
varste.put("Maria", 30);
varste.put("Andrei", 28);
varste.put(null, 35); // Cheie null permisă

// Accesarea valorilor
Integer varstaIon = varste.get("Ion"); // 25

// Verificarea existenței
if (varste.containsKey("Maria")) {
    System.out.println("Maria are " + varste.get("Maria") + " ani");
}

2. LinkedHashMap

LinkedHashMap extinde HashMap și menține ordinea de inserție folosind o listă dublu înlănțuită.

  • ✅ Păstrează ordinea de inserție
  • ✅ Permite configurarea pentru access-order
  • ✅ Ideal pentru implementarea LRU cache
  • ❌ Overhead de memorie mai mare decât HashMap
import java.util.LinkedHashMap;

// LinkedHashMap cu ordine de inserție
LinkedHashMap<String, String> tari = new LinkedHashMap<>();

tari.put("RO", "România");
tari.put("FR", "Franța");
tari.put("DE", "Germania");
tari.put("IT", "Italia");

// Elementele vor fi iterate în ordinea inserării
tari.forEach((cod, nume) -> 
    System.out.println(cod + " -> " + nume)
);

// LRU Cache folosind LinkedHashMap
LinkedHashMap<Integer, String> lruCache = 
    new LinkedHashMap<>(16, 0.75f, true) {
        protected boolean removeEldestEntry(Map.Entry eldest) {
            return size() > 100; // Maxim 100 elemente
        }
    };

3. TreeMap

TreeMap implementează interfețele NavigableMap și SortedMap, stochând elementele sortate.

  • ✅ Chei sortate automat
  • ✅ Implementare Red-Black Tree
  • ✅ Operații O(log n)
  • ❌ Nu permite chei null
import java.util.TreeMap;

// TreeMap cu sortare naturală
TreeMap<Integer, String> note = new TreeMap<>();

note.put(95, "Excelent");
note.put(75, "Bine");
note.put(85, "Foarte bine");
note.put(60, "Satisfăcător");

// Elementele sunt sortate după cheie
System.out.println(note); // {60=Satisfăcător, 75=Bine, 85=Foarte bine, 95=Excelent}

// Operații NavigableMap
System.out.println(note.firstKey());       // 60
System.out.println(note.lastKey());        // 95
System.out.println(note.ceilingKey(80));  // 85 (cea mai mică cheie >= 80)
System.out.println(note.floorKey(80));    // 75 (cea mai mare cheie <= 80)

// SubMap
SortedMap<Integer, String> noteIntre = note.subMap(70, 90);
System.out.println(noteIntre); // {75=Bine, 85=Foarte bine}

4. Hashtable

Hashtable este o clasă legacy, sincronizată și thread-safe.

  • ✅ Thread-safe (sincronizat)
  • ❌ Nu permite null pentru chei sau valori
  • ❌ Mai lent decât HashMap
  • ⚠️ Considerat legacy, folosiți ConcurrentHashMap
import java.util.Hashtable;

Hashtable<String, Double> preturi = new Hashtable<>();

preturi.put("Pâine", 5.50);
preturi.put("Lapte", 8.20);
preturi.put("Ouă", 15.00);

// preturi.put(null, 10.0);  // Aruncă NullPointerException
// preturi.put("Unt", null); // Aruncă NullPointerException

5. ConcurrentHashMap

ConcurrentHashMap este alternativa modernă pentru Hashtable, optimizată pentru concurență.

  • ✅ Thread-safe cu performanță superioară
  • ✅ Permite operații concurente
  • ✅ Segment-based locking
  • ❌ Nu permite null pentru chei sau valori
import java.util.concurrent.ConcurrentHashMap;

ConcurrentHashMap<String, Integer> contor = new ConcurrentHashMap<>();

// Thread-safe operations
contor.put("vizitatori", 0);

// Atomic operations
contor.compute("vizitatori", (k, v) -> v + 1);
contor.computeIfAbsent("utilizatori", k -> 0);
contor.merge("total", 1, Integer::sum);

⚙️ Operații de Bază cu Map

Operații CRUD Complete

import java.util.*;

public class MapOperations {
    public static void main(String[] args) {
        Map<String, Integer> stoc = new HashMap<>();
        
        // CREATE - Adăugare elemente
        stoc.put("Laptop", 15);
        stoc.put("Mouse", 50);
        stoc.put("Tastatură", 30);
        
        // putIfAbsent - adaugă doar dacă cheia nu există
        stoc.putIfAbsent("Monitor", 20);
        stoc.putIfAbsent("Mouse", 100); // Nu va modifica valoarea existentă
        
        // READ - Citire valori
        Integer cantitate = stoc.get("Laptop");
        Integer defaultVal = stoc.getOrDefault("Telefon", 0);
        
        // UPDATE - Actualizare valori
        stoc.put("Laptop", 20); // Suprascrie valoarea
        stoc.replace("Mouse", 45);
        stoc.replace("Tastatură", 30, 35); // Replace doar dacă valoarea e 30
        
        // Compute methods
        stoc.compute("Monitor", (k, v) -> v + 5);
        stoc.computeIfPresent("Laptop", (k, v) -> v * 2);
        stoc.computeIfAbsent("Cablu", k -> 100);
        
        // DELETE - Ștergere elemente
        stoc.remove("Cablu");
        stoc.remove("Mouse", 45); // Șterge doar dacă valoarea e 45
        
        // Verificări
        boolean areLaptop = stoc.containsKey("Laptop");
        boolean areValoare20 = stoc.containsValue(20);
        boolean esteGol = stoc.isEmpty();
        int dimensiune = stoc.size();
    }
}

Iterarea prin Map

Map<String, Integer> puncte = new HashMap<>();
puncte.put("Echipa A", 45);
puncte.put("Echipa B", 38);
puncte.put("Echipa C", 52);

// 1. Iterare prin entrySet() - Cel mai eficient
for (Map.Entry<String, Integer> entry : puncte.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

// 2. Iterare prin keySet()
for (String echipa : puncte.keySet()) {
    System.out.println(echipa + ": " + puncte.get(echipa));
}

// 3. Iterare prin values()
for (Integer punctaj : puncte.values()) {
    System.out.println("Punctaj: " + punctaj);
}

// 4. Java 8+ forEach cu lambda
puncte.forEach((echipa, punctaj) -> 
    System.out.println(echipa + " are " + punctaj + " puncte")
);

// 5. Java 8+ Stream API
puncte.entrySet().stream()
    .filter(e -> e.getValue() > 40)
    .forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));
put(K key, V value)

Adaugă sau actualizează o pereche cheie-valoare

get(Object key)

Returnează valoarea asociată cheii

remove(Object key)

Șterge perechea cheie-valoare

containsKey(Object key)

Verifică dacă cheia există

containsValue(Object value)

Verifică dacă valoarea există

size()

Returnează numărul de elemente

isEmpty()

Verifică dacă Map-ul este gol

clear()

Șterge toate elementele

keySet()

Returnează Set-ul de chei

values()

Returnează Collection de valori

entrySet()

Returnează Set de Entry-uri

putAll(Map m)

Adaugă toate elementele din alt Map

💻 Exemple Practice

Exemplu 1: Numărarea Frecvenței Cuvintelor

import java.util.*;

public class FrecventaCuvinte {
    public static void main(String[] args) {
        String text = "java este un limbaj de programare java este popular";
        String[] cuvinte = text.split(" ");
        
        Map<String, Integer> frecventa = new HashMap<>();
        
        // Metoda clasică
        for (String cuvant : cuvinte) {
            if (frecventa.containsKey(cuvant)) {
                frecventa.put(cuvant, frecventa.get(cuvant) + 1);
            } else {
                frecventa.put(cuvant, 1);
            }
        }
        
        // Metoda Java 8+
        Map<String, Integer> frecventa2 = new HashMap<>();
        for (String cuvant : cuvinte) {
            frecventa2.merge(cuvant, 1, Integer::sum);
        }
        
        // Afișare sortată după frecvență
        frecventa.entrySet().stream()
            .sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
            .forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));
    }
}

Exemplu 2: Gruparea Datelor

class Student {
    String nume;
    String facultate;
    int varsta;
    
    public Student(String nume, String facultate, int varsta) {
        this.nume = nume;
        this.facultate = facultate;
        this.varsta = varsta;
    }
}

public class GrupareStudenti {
    public static void main(String[] args) {
        List<Student> studenti = Arrays.asList(
            new Student("Ana", "Informatică", 20),
            new Student("Ion", "Matematică", 21),
            new Student("Maria", "Informatică", 22),
            new Student("Dan", "Fizică", 20),
            new Student("Elena", "Informatică", 21)
        );
        
        // Grupare după facultate
        Map<String, List<Student>> grupeDupăFacultate = new HashMap<>();
        
        for (Student student : studenti) {
            grupeDupăFacultate
                .computeIfAbsent(student.facultate, k -> new ArrayList<>())
                .add(student);
        }
        
        // Java 8+ Stream API
        Map<String, List<Student>> grupe = studenti.stream()
            .collect(Collectors.groupingBy(s -> s.facultate));
        
        // Afișare
        grupe.forEach((facultate, lista) -> {
            System.out.println(facultate + ": " + lista.size() + " studenți");
        });
    }
}

Exemplu 3: Cache Implementation

import java.util.*;

class SimpleCache<K, V> {
    private final int maxSize;
    private final LinkedHashMap<K, V> cache;
    
    public SimpleCache(int maxSize) {
        this.maxSize = maxSize;
        this.cache = new LinkedHashMap<K, V>(16, 0.75f, true) {
            protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
                return size() > SimpleCache.this.maxSize;
            }
        };
    }
    
    public synchronized V get(K key) {
        return cache.get(key);
    }
    
    public synchronized void put(K key, V value) {
        cache.put(key, value);
    }
    
    public synchronized void clear() {
        cache.clear();
    }
    
    public synchronized int size() {
        return cache.size();
    }
}

public class TestCache {
    public static void main(String[] args) {
        SimpleCache<String, String> cache = new SimpleCache<>(3);
        
        cache.put("user1", "Ion Popescu");
        cache.put("user2", "Maria Ionescu");
        cache.put("user3", "Ana Popa");
        cache.put("user4", "Dan Mihai"); // user1 va fi eliminat
        
        System.out.println(cache.get("user1")); // null
        System.out.println(cache.get("user2")); // Maria Ionescu
    }
}

Exemplu 4: Map de Map-uri (Structuri Complexe)

// Exemplu: Stocarea notelor studenților pe materii și semestre
Map<String, Map<String, Double>> catalog = new HashMap<>();

// Adăugare note pentru studenți
catalog.computeIfAbsent("Ion Popescu", k -> new HashMap<>())
       .put("Matematică", 9.5);
       
catalog.computeIfAbsent("Ion Popescu", k -> new HashMap<>())
       .put("Informatică", 10.0);

catalog.computeIfAbsent("Maria Ionescu", k -> new HashMap<>())
       .put("Matematică", 8.5);

// Calcularea mediei pentru fiecare student
catalog.forEach((student, note) -> {
    double medie = note.values().stream()
        .mapToDouble(Double::doubleValue)
        .average()
        .orElse(0.0);
    System.out.println(student + ": Media = " + medie);
});

🧩 Probleme Practice cu Map

Consolidează-ți cunoștințele rezolvând aceste probleme clasice care apar frecvent în interviuri și aplicații reale.

🟢 Probleme Ușoare

1. Two Sum (LeetCode #1)

Problemă: Găsește două numere într-un array care adunate dau o sumă țintă.

public int[] twoSum(int[] nums, int target) {
    // Folosim HashMap pentru a stoca valoarea și indexul
    Map<Integer, Integer> map = new HashMap<>();
    
    for (int i = 0; i < nums.length; i++) {
        int complement = target - nums[i];
        
        // Verificăm dacă complementul există în map
        if (map.containsKey(complement)) {
            return new int[] {map.get(complement), i};
        }
        
        // Adăugăm numărul curent în map
        map.put(nums[i], i);
    }
    
    return new int[] {}; // Nu s-a găsit soluție
}

// Exemplu de utilizare:
int[] nums = {2, 7, 11, 15};
int target = 9;
int[] result = twoSum(nums, target);
System.out.println(Arrays.toString(result)); // [0, 1]

2. Primul Caracter Unic

Problemă: Găsește primul caracter care apare o singură dată într-un string.

public char firstUniqueChar(String s) {
    // Numărăm frecvența fiecărui caracter
    Map<Character, Integer> frecventa = new LinkedHashMap<>();
    
    for (char c : s.toCharArray()) {
        frecventa.put(c, frecventa.getOrDefault(c, 0) + 1);
    }
    
    // Găsim primul caracter cu frecvența 1
    for (Map.Entry<Character, Integer> entry : frecventa.entrySet()) {
        if (entry.getValue() == 1) {
            return entry.getKey();
        }
    }
    
    return '\0'; // Nu există caracter unic
}

// Test:
System.out.println(firstUniqueChar("leetcode"));    // 'l'
System.out.println(firstUniqueChar("loveleetcode")); // 'v'

3. Verificare Anagramă

Problemă: Verifică dacă două string-uri sunt anagrame (conțin aceleași caractere în aceeași frecvență).

public boolean isAnagram(String s1, String s2) {
    if (s1.length() != s2.length()) {
        return false;
    }
    
    Map<Character, Integer> map = new HashMap<>();
    
    // Adăugăm caracterele din primul string
    for (char c : s1.toCharArray()) {
        map.put(c, map.getOrDefault(c, 0) + 1);
    }
    
    // Scădem caracterele din al doilea string
    for (char c : s2.toCharArray()) {
        if (!map.containsKey(c)) {
            return false;
        }
        map.put(c, map.get(c) - 1);
        if (map.get(c) < 0) {
            return false;
        }
    }
    
    // Verificăm dacă toate valorile sunt 0
    return map.values().stream().allMatch(count -> count == 0);
}

// Test:
System.out.println(isAnagram("anagram", "nagaram")); // true
System.out.println(isAnagram("rat", "car"));         // false

🟡 Probleme Medii

4. Group Anagrams (LeetCode #49)

Problemă: Grupează string-urile care sunt anagrame între ele.

public List<List<String>> groupAnagrams(String[] strs) {
    Map<String, List<String>> map = new HashMap<>();
    
    for (String str : strs) {
        // Sortăm caracterele pentru a obține cheia
        char[] chars = str.toCharArray();
        Arrays.sort(chars);
        String key = String.valueOf(chars);
        
        // Adăugăm string-ul la grupul corespunzător
        map.computeIfAbsent(key, k -> new ArrayList<>()).add(str);
    }
    
    return new ArrayList<>(map.values());
}

// Alternativă optimizată cu frecvență caractere:
public List<List<String>> groupAnagramsOptimized(String[] strs) {
    Map<String, List<String>> map = new HashMap<>();
    
    for (String str : strs) {
        // Creăm o cheie bazată pe frecvența caracterelor
        int[] count = new int[26];
        for (char c : str.toCharArray()) {
            count[c - 'a']++;
        }
        
        StringBuilder key = new StringBuilder();
        for (int i = 0; i < 26; i++) {
            key.append('#').append(count[i]);
        }
        
        map.computeIfAbsent(key.toString(), k -> new ArrayList<>()).add(str);
    }
    
    return new ArrayList<>(map.values());
}

// Test:
String[] input = {"eat", "tea", "tan", "ate", "nat", "bat"};
System.out.println(groupAnagrams(input));
// Output: [["eat","tea","ate"], ["tan","nat"], ["bat"]]

5. Subarray Sum Equals K (LeetCode #560)

Problemă: Găsește numărul de subarray-uri continue care au suma egală cu K.

public int subarraySum(int[] nums, int k) {
    Map<Integer, Integer> sumFreq = new HashMap<>();
    sumFreq.put(0, 1); // Suma 0 apare o dată (array gol)
    
    int currentSum = 0;
    int count = 0;
    
    for (int num : nums) {
        currentSum += num;
        
        // Verificăm dacă există o sumă prefix care,
        // scăzută din suma curentă, dă k
        if (sumFreq.containsKey(currentSum - k)) {
            count += sumFreq.get(currentSum - k);
        }
        
        // Adăugăm suma curentă în map
        sumFreq.put(currentSum, sumFreq.getOrDefault(currentSum, 0) + 1);
    }
    
    return count;
}

// Test:
int[] nums = {1, 1, 1};
System.out.println(subarraySum(nums, 2)); // 2 (subarray-urile [1,1] de 2 ori)

6. Longest Substring Without Repeating Characters (LeetCode #3)

Problemă: Găsește lungimea celui mai lung substring fără caractere repetate.

public int lengthOfLongestSubstring(String s) {
    Map<Character, Integer> lastIndex = new HashMap<>();
    int maxLength = 0;
    int start = 0;
    
    for (int end = 0; end < s.length(); end++) {
        char c = s.charAt(end);
        
        // Dacă caracterul a mai apărut și e în fereastra curentă
        if (lastIndex.containsKey(c) && lastIndex.get(c) >= start) {
            start = lastIndex.get(c) + 1;
        }
        
        lastIndex.put(c, end);
        maxLength = Math.max(maxLength, end - start + 1);
    }
    
    return maxLength;
}

// Varianta cu Set pentru vizualizare mai clară:
public int lengthOfLongestSubstringSet(String s) {
    Set<Character> window = new HashSet<>();
    int maxLength = 0;
    int left = 0, right = 0;
    
    while (right < s.length()) {
        if (!window.contains(s.charAt(right))) {
            window.add(s.charAt(right));
            maxLength = Math.max(maxLength, right - left + 1);
            right++;
        } else {
            window.remove(s.charAt(left));
            left++;
        }
    }
    
    return maxLength;
}

// Test:
System.out.println(lengthOfLongestSubstring("abcabcbb")); // 3 ("abc")
System.out.println(lengthOfLongestSubstring("bbbbb"));    // 1 ("b")

🔴 Probleme Avansate

7. LRU Cache (LeetCode #146)

Problemă: Implementează un Least Recently Used (LRU) cache cu complexitate O(1) pentru get și put.

class LRUCache {
    class Node {
        int key, value;
        Node prev, next;
        
        Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }
    
    private final int capacity;
    private final Map<Integer, Node> cache;
    private final Node head, tail;
    
    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.cache = new HashMap<>();
        
        // Noduri santinelă pentru lista dublu înlănțuită
        this.head = new Node(0, 0);
        this.tail = new Node(0, 0);
        head.next = tail;
        tail.prev = head;
    }
    
    public int get(int key) {
        if (!cache.containsKey(key)) {
            return -1;
        }
        
        Node node = cache.get(key);
        removeNode(node);
        addToHead(node);
        return node.value;
    }
    
    public void put(int key, int value) {
        if (cache.containsKey(key)) {
            Node node = cache.get(key);
            node.value = value;
            removeNode(node);
            addToHead(node);
        } else {
            if (cache.size() == capacity) {
                Node lru = tail.prev;
                removeNode(lru);
                cache.remove(lru.key);
            }
            Node newNode = new Node(key, value);
            cache.put(key, newNode);
            addToHead(newNode);
        }
    }
    
    private void removeNode(Node node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }
    
    private void addToHead(Node node) {
        node.next = head.next;
        node.prev = head;
        head.next.prev = node;
        head.next = node;
    }
}

8. Word Pattern (LeetCode #290)

Problemă: Verifică dacă un string urmează un anumit pattern.

public boolean wordPattern(String pattern, String s) {
    String[] words = s.split(" ");
    
    if (pattern.length() != words.length) {
        return false;
    }
    
    Map<Character, String> charToWord = new HashMap<>();
    Map<String, Character> wordToChar = new HashMap<>();
    
    for (int i = 0; i < pattern.length(); i++) {
        char c = pattern.charAt(i);
        String word = words[i];
        
        // Verificăm maparea bidirecțională
        if (charToWord.containsKey(c)) {
            if (!charToWord.get(c).equals(word)) {
                return false;
            }
        } else {
            charToWord.put(c, word);
        }
        
        if (wordToChar.containsKey(word)) {
            if (!wordToChar.get(word).equals(c)) {
                return false;
            }
        } else {
            wordToChar.put(word, c);
        }
    }
    
    return true;
}

// Test:
System.out.println(wordPattern("abba", "dog cat cat dog")); // true
System.out.println(wordPattern("abba", "dog cat cat fish")); // false

9. Top K Frequent Elements (LeetCode #347)

Problemă: Găsește cele mai frecvente K elemente dintr-un array.

public int[] topKFrequent(int[] nums, int k) {
    // Pasul 1: Calculăm frecvența
    Map<Integer, Integer> freq = new HashMap<>();
    for (int num : nums) {
        freq.put(num, freq.getOrDefault(num, 0) + 1);
    }
    
    // Pasul 2: Bucket sort - grupăm după frecvență
    List<Integer>[] buckets = new List[nums.length + 1];
    for (Map.Entry<Integer, Integer> entry : freq.entrySet()) {
        int frequency = entry.getValue();
        if (buckets[frequency] == null) {
            buckets[frequency] = new ArrayList<>();
        }
        buckets[frequency].add(entry.getKey());
    }
    
    // Pasul 3: Colectăm top K elemente
    List<Integer> result = new ArrayList<>();
    for (int i = buckets.length - 1; i >= 0 && result.size() < k; i--) {
        if (buckets[i] != null) {
            result.addAll(buckets[i]);
        }
    }
    
    return result.stream().mapToInt(i -> i).toArray();
}

// Alternativă cu PriorityQueue:
public int[] topKFrequentHeap(int[] nums, int k) {
    Map<Integer, Integer> freq = new HashMap<>();
    for (int num : nums) {
        freq.put(num, freq.getOrDefault(num, 0) + 1);
    }
    
    PriorityQueue<Integer> heap = new PriorityQueue<>(
        (a, b) -> freq.get(b) - freq.get(a)
    );
    
    heap.addAll(freq.keySet());
    
    int[] result = new int[k];
    for (int i = 0; i < k; i++) {
        result[i] = heap.poll();
    }
    return result;
}
Sfaturi pentru Rezolvarea Problemelor cu Map:
  • 🎯 Identifică pattern-ul: frecvență, grupare, mapare bidirecțională
  • 🔑 Alege cheia potrivită: ce informație trebuie accesată rapid?
  • ⚡ Optimizează cu compute methods: computeIfAbsent, merge
  • 🔄 Pentru sliding window: folosește Map pentru a urmări elementele din fereastră
  • 📊 Pentru counting: Map<Element, Integer> sau Map<Element, Long>
  • 🎨 Pentru grupare: Map<Key, List<Value>>
Complexitate Timp și Spațiu:
  • HashMap: O(1) amortizat pentru operații de bază
  • TreeMap: O(log n) pentru operații de bază
  • Spațiu: O(n) unde n este numărul de perechi cheie-valoare
  • Atenție la collision handling în HashMap - worst case O(n)

📊 Comparație între Implementări

Caracteristică HashMap LinkedHashMap TreeMap Hashtable ConcurrentHashMap
Ordine Fără ordine Ordine inserție Sortată Fără ordine Fără ordine
Null Keys ✅ Da (unul) ✅ Da (unul) ❌ Nu ❌ Nu ❌ Nu
Null Values ✅ Da ✅ Da ✅ Da ❌ Nu ❌ Nu
Thread-Safe ❌ Nu ❌ Nu ❌ Nu ✅ Da ✅ Da
Performanță O(1) O(1) O(log n) O(1) O(1)
Overhead Memorie Mic Mediu Mare Mediu Mare
Când să folosești Uz general Ordine necesară Sortare necesară Legacy code Concurență
Sfat: Pentru majoritatea cazurilor, HashMap este alegerea ideală. Folosiți LinkedHashMap când ordinea contează, TreeMap pentru sortare automată, și ConcurrentHashMap pentru aplicații multi-threaded.

✨ Best Practices și Sfaturi

1. Alegeți Implementarea Corectă

HashMap pentru performanță, LinkedHashMap pentru ordine, TreeMap pentru sortare, ConcurrentHashMap pentru thread-safety.

2. Inițializați cu Capacitate

Pentru Map-uri mari, specificați capacitatea inițială pentru a evita redimensionările: new HashMap<>(1000)

3. Implementați equals() și hashCode()

Pentru chei custom, implementați corect metodele equals() și hashCode() pentru funcționare corectă.

4. Folosiți Immutable Keys

Cheile ar trebui să fie immutable pentru a evita comportamente neprevăzute.

5. Null Safety

Folosiți getOrDefault() și computeIfAbsent() pentru a evita NullPointerException.

6. Java 8+ Features

Utilizați metodele compute(), merge(), și forEach() pentru cod mai curat și expresiv.

Exemple de Anti-Pattern-uri de Evitat

Evitați aceste greșeli comune:
  • Nu modificați obiectele folosite ca chei după inserare
  • Nu iterați și modificați simultan Map-ul (folosiți Iterator)
  • Nu presupuneți ordinea în HashMap
  • Nu folosiți Hashtable în cod nou (preferați ConcurrentHashMap)

Cod pentru Evitarea ConcurrentModificationException

Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);

// GREȘIT - va arunca ConcurrentModificationException
// for (String key : map.keySet()) {
//     if (map.get(key) == 2) {
//         map.remove(key);
//     }
// }

// CORECT - Folosind Iterator
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry<String, Integer> entry = iterator.next();
    if (entry.getValue() == 2) {
        iterator.remove();
    }
}

// CORECT - Java 8+ removeIf
map.entrySet().removeIf(entry -> entry.getValue() == 2);

🎮 Demo Interactiv - Rezolvă o Problemă

Apasă butoanele pentru a vedea diferite exemple de Map în acțiune: