Ce face HashMap::entry
Foarte des vrei: „dacă cheia există, modific valoarea; dacă nu, o inserez”. Fără entry, scrii if let Some(n) = m.get_mut(k) { *n += 1 } else { m.insert(k, 1); }: două căutări și cod lung. m.entry(k) caută cheia o singură dată și returnează un Entry, adică locul cheii în dicționar, ocupat sau liber.
Entry este un enum cu două variante: Occupied (cheia există, ai acces la valoarea ei) și Vacant (cheia lipsește, poți insera). De cele mai multe ori nu faci match pe el, ci apelezi direct o metodă: or_insert(v), or_insert_with(|| ...), or_default() (inserează dacă lipsește și îți dau &mut V) sau and_modify(|v| ...) (modifică dacă există). Cea mai cunoscută formă: *m.entry(cuvant).or_insert(0) += 1;.
entry primește cheia prin valoare (de exemplu un String), pentru că s-ar putea să trebuiască inserată. Cât timp ții un Entry sau referința primită de la el, dicționarul este împrumutat mutabil. BTreeMap are exact același API (btree_map::Entry), deci tot ce e pe această pagină merge și acolo.
Parametri
| Parametru | Descriere |
|---|---|
key |
Cheia căutată, dată prin valoare (este mutată în dicționar doar dacă se inserează). |
Ce returnează
Un Entry<'_, K, V>: Entry::Occupied dacă cheia există, altfel Entry::Vacant.
Exemple
Exemplul 1: Frecvența cuvintelor dintr-un text
Împărțim textul la orice caracter care nu e literă sau cifră și numărăm fiecare cuvânt, cu litere mici. or_insert(0) pune 0 la prima apariție și returnează &mut u32, pe care îl mărim. La final sortăm după frecvență, apoi alfabetic.
use std::collections::HashMap;
fn main() {
let text = "Ana are mere. Mihai are pere, iar Ana are și prune. \
Mere, pere, prune: Ana le duce pe toate la piață, la Iași.";
let mut frecventa: HashMap<String, u32> = HashMap::new();
for cuvant in text.split(|c: char| !c.is_alphanumeric()) {
if cuvant.is_empty() {
continue;
}
*frecventa.entry(cuvant.to_lowercase()).or_insert(0) += 1;
}
let mut lista: Vec<(&String, &u32)> = frecventa.iter().collect();
lista.sort_by(|a, b| b.1.cmp(a.1).then(a.0.cmp(b.0)));
for (cuvant, n) in lista.iter().take(6) {
println!("{n} × {cuvant}");
}
println!("{} cuvinte distincte", frecventa.len());
}
Rezultat
3 × ana
3 × are
2 × la
2 × mere
2 × pere
2 × prune
15 cuvinte distincte
Exemplul 2: match pe Entry: Occupied sau Vacant
Când comportamentul diferă mult între cele două cazuri, faci match. VacantEntry::insert pune valoarea; OccupiedEntry::get o citește.
use std::collections::HashMap;
use std::collections::hash_map::Entry;
fn main() {
let mut inscrieri: HashMap<&str, u32> = HashMap::new();
for (nume, varsta) in [("Ana", 17), ("Mihai", 18), ("Ana", 19)] {
match inscrieri.entry(nume) {
Entry::Vacant(loc) => {
loc.insert(varsta);
println!("{nume} a fost înscris(ă), {varsta} ani");
}
Entry::Occupied(existent) => {
println!("{nume} este deja înscris(ă), cu vârsta {}", existent.get());
}
}
}
println!("{} persoane înscrise", inscrieri.len());
}
Rezultat
Ana a fost înscris(ă), 17 ani
Mihai a fost înscris(ă), 18 ani
Ana este deja înscris(ă), cu vârsta 17
2 persoane înscrise
Exemplul 3: Elevi grupați pe orașe
or_default() creează un Vec gol pentru orașul nou și returnează &mut Vec, în care adăugăm direct.
use std::collections::HashMap;
fn main() {
let elevi = [("Ana", "Iași"), ("Mihai", "Cluj"), ("Ioana", "Iași"), ("Radu", "Brașov"), ("Elena", "Cluj")];
let mut pe_orase: HashMap<&str, Vec<&str>> = HashMap::new();
for (nume, oras) in elevi {
pe_orase.entry(oras).or_default().push(nume);
}
let mut orase: Vec<_> = pe_orase.keys().copied().collect();
orase.sort();
for o in orase {
println!("{o}: {}", pe_orase[o].join(", "));
}
}
Rezultat
Brașov: Radu
Cluj: Mihai, Elena
Iași: Ana, Ioana
Greșeli frecvente
- Scrii
m.entry(&cuvant)pentru unHashMap<String, _>:entryvrea cheia prin valoare (expected `String`, found `&String`). Dăcuvant.to_string()saucuvant.clone(); pentru chei&str, dă direct&str-ul. - Uiți steluța:
m.entry(k).or_insert(0) += 1nu compilează, pentru căor_insertreturnează&mut V. Scrie*m.entry(k).or_insert(0) += 1. - Păstrezi referința primită de la
or_insertși apoi folosești dicționarul: cannot borrow `m` as immutable because it is also borrowed as mutable. Termină lucrul cu referința înainte. - Faci
contains_key+insert, sauget_mut+insert: două căutări și cod mai lung.entryle înlocuiește pe amândouă.
Comenzi înrudite
Entry::or_insert: Dacă cheia lipsește, inserează valoarea dată; în ambele cazuri returnează o referință mutabilă la valoarea din dicționar.Entry::or_insert_with: Ca or_insert, dar valoarea de inserat e calculată de un closure, apelat doar dacă cheia lipsește.Entry::or_default: Dacă cheia lipsește, inserează valoarea implicită a tipului (0, vector gol, text gol) și returnează &mut V.Entry::and_modify: Dacă cheia există, aplică funcția dată pe valoarea ei; returnează Entry-ul, ca să poți continua cu or_insert.HashMap::insert: Pune o pereche cheie-valoare în dicționar; dacă cheia exista, înlocuiește valoarea și o returnează pe cea veche.BTreeMap: Dicționar cu cheile mereu sortate: aceleași operații ca HashMap, dar parcurgerea și afișarea sunt în ordinea cheilor.