Skip to content

Commit

Permalink
Auto merge of #38017 - arthurprs:hm-extend, r=bluss
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bors committed Dec 6, 2016
2 parents b5d0f90 + 2c5d240 commit 5f128ed
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 11 deletions.
17 changes: 13 additions & 4 deletions src/libstd/collections/hash/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1974,10 +1974,8 @@ impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S>
S: BuildHasher + Default
{
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> HashMap<K, V, S> {
let iterator = iter.into_iter();
let lower = iterator.size_hint().0;
let mut map = HashMap::with_capacity_and_hasher(lower, Default::default());
map.extend(iterator);
let mut map = HashMap::with_hasher(Default::default());
map.extend(iter);
map
}
}
Expand All @@ -1988,6 +1986,17 @@ impl<K, V, S> Extend<(K, V)> for HashMap<K, V, S>
S: BuildHasher
{
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
// Keys may be already present or show multiple times in the iterator.
// Reserve the entire hint lower bound if the map is empty.
// Otherwise reserve half the hint (rounded up), so the map
// will only resize twice in the worst case.
let iter = iter.into_iter();
let reserve = if self.is_empty() {
iter.size_hint().0
} else {
(iter.size_hint().0 + 1) / 2
};
self.reserve(reserve);
for (k, v) in iter {
self.insert(k, v);
}
Expand Down
10 changes: 3 additions & 7 deletions src/libstd/collections/hash/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -663,10 +663,8 @@ impl<T, S> FromIterator<T> for HashSet<T, S>
S: BuildHasher + Default
{
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> HashSet<T, S> {
let iterator = iter.into_iter();
let lower = iterator.size_hint().0;
let mut set = HashSet::with_capacity_and_hasher(lower, Default::default());
set.extend(iterator);
let mut set = HashSet::with_hasher(Default::default());
set.extend(iter);
set
}
}
Expand All @@ -677,9 +675,7 @@ impl<T, S> Extend<T> for HashSet<T, S>
S: BuildHasher
{
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for k in iter {
self.insert(k);
}
self.map.extend(iter.into_iter().map(|k| (k, ())));
}
}

Expand Down

0 comments on commit 5f128ed

Please sign in to comment.