Skip to content

Commit 5f128ed

Browse files
committed
Auto merge of #38017 - arthurprs:hm-extend, r=bluss
Smarter HashMap/HashSet pre-allocation for extend/from_iter HashMap/HashSet from_iter and extend are making totally different assumptions. A more balanced decision may allocate half the lower hint (rounding up). For "well defined" iterators this effectively limits the worst case to two resizes (the initial reserve + one resize). cc #36579 cc @bluss
2 parents b5d0f90 + 2c5d240 commit 5f128ed

File tree

2 files changed

+16
-11
lines changed

2 files changed

+16
-11
lines changed

src/libstd/collections/hash/map.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -1974,10 +1974,8 @@ impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S>
19741974
S: BuildHasher + Default
19751975
{
19761976
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> HashMap<K, V, S> {
1977-
let iterator = iter.into_iter();
1978-
let lower = iterator.size_hint().0;
1979-
let mut map = HashMap::with_capacity_and_hasher(lower, Default::default());
1980-
map.extend(iterator);
1977+
let mut map = HashMap::with_hasher(Default::default());
1978+
map.extend(iter);
19811979
map
19821980
}
19831981
}
@@ -1988,6 +1986,17 @@ impl<K, V, S> Extend<(K, V)> for HashMap<K, V, S>
19881986
S: BuildHasher
19891987
{
19901988
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
1989+
// Keys may be already present or show multiple times in the iterator.
1990+
// Reserve the entire hint lower bound if the map is empty.
1991+
// Otherwise reserve half the hint (rounded up), so the map
1992+
// will only resize twice in the worst case.
1993+
let iter = iter.into_iter();
1994+
let reserve = if self.is_empty() {
1995+
iter.size_hint().0
1996+
} else {
1997+
(iter.size_hint().0 + 1) / 2
1998+
};
1999+
self.reserve(reserve);
19912000
for (k, v) in iter {
19922001
self.insert(k, v);
19932002
}

src/libstd/collections/hash/set.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -663,10 +663,8 @@ impl<T, S> FromIterator<T> for HashSet<T, S>
663663
S: BuildHasher + Default
664664
{
665665
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> HashSet<T, S> {
666-
let iterator = iter.into_iter();
667-
let lower = iterator.size_hint().0;
668-
let mut set = HashSet::with_capacity_and_hasher(lower, Default::default());
669-
set.extend(iterator);
666+
let mut set = HashSet::with_hasher(Default::default());
667+
set.extend(iter);
670668
set
671669
}
672670
}
@@ -677,9 +675,7 @@ impl<T, S> Extend<T> for HashSet<T, S>
677675
S: BuildHasher
678676
{
679677
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
680-
for k in iter {
681-
self.insert(k);
682-
}
678+
self.map.extend(iter.into_iter().map(|k| (k, ())));
683679
}
684680
}
685681

0 commit comments

Comments
 (0)