diff --git a/CHANGELOG.md b/CHANGELOG.md
index d618bea..7cf80f6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added
- Added ZIP features for examining the contents of an archive
- Added FileNameEqualityComparer
+ - Added BiDictionary for one-to-one maps
## [3.5.1] - 2018-06-04
### Changed
diff --git a/ICD.Common.Utils/Collections/BiDictionary.cs b/ICD.Common.Utils/Collections/BiDictionary.cs
new file mode 100644
index 0000000..b9770b6
--- /dev/null
+++ b/ICD.Common.Utils/Collections/BiDictionary.cs
@@ -0,0 +1,184 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace ICD.Common.Utils.Collections
+{
+ ///
+ /// Provides a 1-to-1 map of keys to values with O(1) Value->Key lookup time.
+ ///
+ ///
+ ///
+ public sealed class BiDictionary : IDictionary
+ {
+ private readonly Dictionary m_KeyToValue;
+ private readonly Dictionary m_ValueToKey;
+
+ #region Properties
+
+ public int Count { get { return m_KeyToValue.Count; } }
+
+ public bool IsReadOnly { get { return false; } }
+
+ public ICollection Keys { get { return m_KeyToValue.Keys; } }
+
+ public ICollection Values { get { return m_ValueToKey.Keys; } }
+
+ #endregion
+
+ ///
+ /// Constructor.
+ ///
+ public BiDictionary()
+ {
+ m_KeyToValue = new Dictionary();
+ m_ValueToKey = new Dictionary();
+ }
+
+ #region Methods
+
+ public void Clear()
+ {
+ m_KeyToValue.Clear();
+ m_ValueToKey.Clear();
+ }
+
+ public bool ContainsKey(TKey key)
+ {
+ return m_KeyToValue.ContainsKey(key);
+ }
+
+ public bool ContainsValue(TValue value)
+ {
+ return m_ValueToKey.ContainsKey(value);
+ }
+
+ public void Add(TKey key, TValue value)
+ {
+// ReSharper disable once CompareNonConstrainedGenericWithNull
+ if (key == null)
+ throw new ArgumentNullException("key");
+
+// ReSharper disable once CompareNonConstrainedGenericWithNull
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ if (m_KeyToValue.ContainsKey(key))
+ throw new ArgumentException("Key is already present in the dictionary", "key");
+
+ if (m_ValueToKey.ContainsKey(value))
+ throw new ArgumentException("Value is already present in the collection", "value");
+
+ m_KeyToValue.Add(key, value);
+ m_ValueToKey.Add(value, key);
+ }
+
+ public void Set(TKey key, TValue value)
+ {
+// ReSharper disable once CompareNonConstrainedGenericWithNull
+ if (key == null)
+ throw new ArgumentNullException("key");
+
+// ReSharper disable once CompareNonConstrainedGenericWithNull
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ m_KeyToValue[key] = value;
+ m_ValueToKey[value] = key;
+ }
+
+ public TKey GetKey(TValue value)
+ {
+ return m_ValueToKey[value];
+ }
+
+ public TValue GetValue(TKey key)
+ {
+ return m_KeyToValue[key];
+ }
+
+ public bool RemoveKey(TKey key)
+ {
+ if (!ContainsKey(key))
+ return false;
+
+ TValue value = m_KeyToValue[key];
+
+ m_KeyToValue.Remove(key);
+ m_ValueToKey.Remove(value);
+
+ return true;
+ }
+
+ public bool RemoveValue(TValue value)
+ {
+ if (!ContainsValue(value))
+ return false;
+
+ TKey key = m_ValueToKey[value];
+
+ return RemoveKey(key);
+ }
+
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ return m_KeyToValue.TryGetValue(key, out value);
+ }
+
+ public bool TryGetKey(TValue value, out TKey key)
+ {
+ return m_ValueToKey.TryGetValue(value, out key);
+ }
+
+ #endregion
+
+ #region IDictionary
+
+ TValue IDictionary.this[TKey key] { get { return GetValue(key); } set { Set(key, value); } }
+
+ bool IDictionary.Remove(TKey key)
+ {
+ return RemoveKey(key);
+ }
+
+ #endregion
+
+ #region ICollection
+
+ void ICollection>.Add(KeyValuePair item)
+ {
+ Add(item.Key, item.Value);
+ }
+
+ bool ICollection>.Contains(KeyValuePair item)
+ {
+ return (m_KeyToValue as IDictionary).Contains(item);
+ }
+
+ bool ICollection>.Remove(KeyValuePair item)
+ {
+ return RemoveKey(item.Key);
+ }
+
+ void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ (m_KeyToValue as IDictionary).CopyTo(array, arrayIndex);
+ }
+
+ #endregion
+
+ #region IEnumerable
+
+ public IEnumerator> GetEnumerator()
+ {
+ return m_KeyToValue.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #endregion
+ }
+}
diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj
index 9d0bd60..ed696f1 100644
--- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj
+++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj
@@ -74,6 +74,7 @@
+