[−][src]Module incremental_topo::bimap
A fast two-way bijective map.
Disclaimer
This API and documentation is taken directly from Billy Rieger's
bimap-rs
. Specific changes have been made to the implementation such
that left and right values can be searched for using Borrow
ed versions of
themselves.
Before the change the get_by_left
function signature looked like this:
pub fn get_by_left(&self, left: &L) -> Option<&R>;
After the change the get_by_left
function signature looks like this:
pub fn get_by_left<P>(&self, left: &P) -> Option<&R>
where
L: Borrow<P>,
P: Hash + Eq + ?Sized,
In order to accomodate this change, the internal representation of the bimap
changed slightly. Previously bimap used an internal representation of two
hashmaps that mapped Rc<L> -> Rc<R>
and Rc<R> -> Rc<L>
. The new version
also has two hashmaps that map hash(L) -> R
and hash(R) -> L
.
Overall, this version accomplishes the bare minimum needed for a bimap,
while bimap-rs
offers more options.
Description
A BiMap<L, R>
is a bijective map between values of type L
, called
left values, and values of type R
, called right values. This means every
left value is associated with exactly one right value and vice versa.
Compare this to a HashMap
, where every key is associated with exactly
one value but a value can be associated with more than one key.
Internally, a BiMap
is composed of two HashMap
s, one for the
left-to-right direction and one for right-to-left. As such, the big-O
performance of the get
, remove
, insert
, and contains
methods are
the same as those of a HashMap
.
As with HashMap
, it is considered a logic error to modify a value's hash
while it is in the BiMap
using a Cell
, RefCell
, etc. This is
especially important because both the left and right values are hashed, and
accessible via mutable getters.
Examples
use incremental_topo::bimap::BiMap; let mut elements = BiMap::new(); // insert chemicals and their corresponding symbols elements.insert("hydrogen", "H"); elements.insert("carbon", "C"); elements.insert("bromine", "Br"); elements.insert("neodymium", "Nd"); // retrieve chemical symbol by name (left to right) assert_eq!(elements.get_by_left(&"bromine"), Some(&"Br")); assert_eq!(elements.get_by_left(&"oxygen"), None); // retrieve name by chemical symbol (right to left) assert_eq!(elements.get_by_right(&"C"), Some(&"carbon")); assert_eq!(elements.get_by_right(&"Al"), None); // check membership assert!(elements.contains_left(&"hydrogen")); assert!(!elements.contains_right(&"He")); // remove elements assert_eq!( elements.remove_by_left(&"neodymium"), Some(("neodymium", "Nd")) ); assert_eq!(elements.remove_by_right(&"Nd"), None); // iterate over elements for (left, right) in elements.iter() { println!("the chemical symbol for {} is {}", left, right); }
Insertion and overwriting
Consider the following example:
use incremental_topo::bimap::BiMap; let mut bimap = BiMap::new(); bimap.insert('a', 1); bimap.insert('b', 1); // what to do here?
In order to maintain the bijection, the BiMap
cannot have both ('a', 1)
and ('b', 1)
in the map. Otherwise, the right-value 1
would have two
left values associated with it. Either we should allow the call to insert
to go through and overwrite ('a', 1)
, or not let ('b', 1)
be inserted
at all. BiMap
allows for both possibilities. To insert with overwriting,
use insert
, and to insert without overwriting, use
insert_no_overwrite
. The return type of insert
is the enum
Overwritten
, which indicates what values, if any, were overwritten; the
return type of insert_no_overwrite
is a boolean indicating if the
insertion was successful.
This is especially important when dealing with types that can be equal
while having different data. Unlike a HashMap
, which doesn't update an
equal key upon insertion, a BiMap
updates both the left values and the
right values.
use incremental_topo::bimap::{BiMap, Overwritten}; use std::hash::{Hash, Hasher}; #[derive(Clone, Copy, Debug)] struct Foo { important: char, unimportant: u32, } // equality only depends on the important data impl PartialEq for Foo { fn eq(&self, other: &Foo) -> bool { self.important == other.important } } impl Eq for Foo {} // hash only depends on the important data impl Hash for Foo { fn hash<H: Hasher>(&self, state: &mut H) { self.important.hash(state); } } // create two Foos that are equal but have different data let foo1 = Foo { important: 'a', unimportant: 1, }; let foo2 = Foo { important: 'a', unimportant: 2, }; assert_eq!(foo1, foo2); let mut bimap = BiMap::new(); bimap.insert(foo1, 99); let overwritten = bimap.insert(foo2, 100); // foo1 is overwritten and returned; foo2 is in the bimap assert_eq!(overwritten, Overwritten::Left(foo1, 99)); assert_eq!(bimap.get_by_right(&100), Some(&foo2));
Structs
BiMap | A two-way map between left values and right values. |
Enums
Overwritten | The previous pairs, if any, that were overwritten by a call the |