Implement remove

This commit is contained in:
Gwilym Kuiper 2022-03-17 23:18:49 +00:00
parent 019872b8cc
commit bfebba1ec1

View file

@ -20,13 +20,16 @@ where
K: Sized, K: Sized,
V: Sized, V: Sized,
{ {
fn with_new_key_value(&self, new_key: K, new_value: V) -> Self { fn with_new_key_value(self, new_key: K, new_value: V) -> (Self, V) {
(
Self { Self {
hash: self.hash, hash: self.hash,
distance_to_initial_bucket: self.distance_to_initial_bucket, distance_to_initial_bucket: self.distance_to_initial_bucket,
key: new_key, key: new_key,
value: new_value, value: new_value,
} },
self.value,
)
} }
} }
@ -50,6 +53,10 @@ impl<K, V> HashMap<K, V> {
hasher: Default::default(), hasher: Default::default(),
} }
} }
pub fn len(&self) -> usize {
self.number_of_elements
}
} }
fn fast_mod(len: usize, hash: HashType) -> usize { fn fast_mod(len: usize, hash: HashType) -> usize {
@ -62,7 +69,7 @@ where
K: Eq, K: Eq,
{ {
fn get_location(&self, key: &K, hash: HashType) -> Option<usize> { fn get_location(&self, key: &K, hash: HashType) -> Option<usize> {
for distance_to_initial_bucket in 0..=self.max_distance_to_initial_bucket + 1 { for distance_to_initial_bucket in 0..=self.max_distance_to_initial_bucket {
let location = fast_mod(self.nodes.len(), hash + distance_to_initial_bucket); let location = fast_mod(self.nodes.len(), hash + distance_to_initial_bucket);
let node = &self.nodes[location]; let node = &self.nodes[location];
@ -83,28 +90,44 @@ impl<K, V> HashMap<K, V>
where where
K: Eq + Hash, K: Eq + Hash,
{ {
pub fn put(&mut self, key: K, value: V) { pub fn put(&mut self, key: K, value: V) -> Option<V> {
let mut hasher = self.hasher.build_hasher(); let hash = self.hash(&key);
key.hash(&mut hasher);
let hash = hasher.finish() as HashType;
if let Some(location) = self.get_location(&key, hash) { if let Some(location) = self.get_location(&key, hash) {
let old_node = self.nodes[location].as_ref().unwrap(); let old_node = self.nodes[location].take().unwrap();
self.nodes[location] = Some(old_node.with_new_key_value(key, value)); let (new_node, old_value) = old_node.with_new_key_value(key, value);
self.nodes[location] = Some(new_node);
return; return Some(old_value);
} }
self.insert_new(key, value, hash); self.insert_new(key, value, hash);
None
} }
pub fn get(&self, key: &K) -> Option<&V> { pub fn get(&self, key: &K) -> Option<&V> {
let hash = self.hash(key);
self.get_location(key, hash)
.map(|location| &self.nodes[location].as_ref().unwrap().value)
}
pub fn remove(&mut self, key: &K) -> Option<V> {
let hash = self.hash(key);
self.get_location(key, hash)
.map(|location| self.remove_from_location(location))
}
}
impl<K, V> HashMap<K, V>
where
K: Hash,
{
fn hash(&self, key: &K) -> HashType {
let mut hasher = self.hasher.build_hasher(); let mut hasher = self.hasher.build_hasher();
key.hash(&mut hasher); key.hash(&mut hasher);
let hash = hasher.finish() as HashType; hasher.finish() as HashType
self.get_location(&key, hash)
.map(|location| &self.nodes[location].as_ref().unwrap().value)
} }
} }
@ -144,6 +167,29 @@ impl<K, V> HashMap<K, V> {
self.number_of_elements += 1; self.number_of_elements += 1;
} }
fn remove_from_location(&mut self, location: usize) -> V {
let mut current_location = location;
let result = loop {
let next_location = fast_mod(self.nodes.len(), (current_location + 1) as HashType);
// if the next node is empty, then we can clear the current node
if self.nodes[next_location].is_none() {
break self.nodes[current_location].take().unwrap();
}
self.nodes.swap(current_location, next_location);
self.nodes[current_location]
.as_mut()
.unwrap()
.distance_to_initial_bucket -= 1;
current_location = next_location;
};
self.number_of_elements -= 1;
result.value
}
} }
#[cfg(test)] #[cfg(test)]
@ -152,7 +198,7 @@ mod test {
use crate::Gba; use crate::Gba;
#[test_case] #[test_case]
fn can_store_up_to_initial_capacity_elements(_gba: &mut Gba) { fn can_store_and_retrieve_8_elements(_gba: &mut Gba) {
let mut map = HashMap::new(); let mut map = HashMap::new();
for i in 0..8 { for i in 0..8 {
@ -163,4 +209,43 @@ mod test {
assert_eq!(map.get(&i), Some(&(i % 4))); assert_eq!(map.get(&i), Some(&(i % 4)));
} }
} }
#[test_case]
fn can_get_the_length(_gba: &mut Gba) {
let mut map = HashMap::new();
for i in 0..8 {
map.put(i / 2, true);
}
assert_eq!(map.len(), 4);
}
#[test_case]
fn returns_none_if_element_does_not_exist(_gba: &mut Gba) {
let mut map = HashMap::new();
for i in 0..8 {
map.put(i, i % 3);
}
assert_eq!(map.get(&12), None);
}
#[test_case]
fn can_delete_entries(_gba: &mut Gba) {
let mut map = HashMap::new();
for i in 0..8 {
map.put(i, i % 3);
}
for i in 0..4 {
map.remove(&i);
}
assert_eq!(map.len(), 4);
assert_eq!(map.get(&3), None);
assert_eq!(map.get(&7), Some(&1));
}
} }