PageRenderTime 55ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Collections/MultiDictionary.cs

#
C# | 2291 lines | 1653 code | 202 blank | 436 comment | 89 complexity | d951e5144ba674242e5bfff8b84ef410 MD5 | raw file
Possible License(s): Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. using System;
  2. using System.Collections.Generic;
  3. using Delta.Utilities.Helpers;
  4. using NUnit.Framework;
  5. namespace Delta.Utilities.Collections
  6. {
  7. /// <summary>
  8. /// <para>The MultiDictionary class that associates values with a key.
  9. /// Unlike an Dictionary, each key can have multiple values associated with
  10. /// it. When indexing an MultiDictionary, instead of a single value
  11. /// associated with a key, you retrieve an enumeration of values.</para>
  12. /// <para>When constructed, you can chose to allow the same value to be
  13. /// associated with a key multiple times, or only one time. </para>
  14. /// </summary>
  15. /// <typeparam name="TKey">Key type</typeparam>
  16. /// <typeparam name="TValue">Value type</typeparam>
  17. /// <seealso cref="Dictionary{TKey,TValue}"/>
  18. public class MultiDictionary<TKey, TValue>
  19. : MultiDictionaryBase<TKey, TValue>,
  20. ICloneable<MultiDictionary<TKey, TValue>>
  21. {
  22. #region KeyAndValues Struct
  23. /// <summary>
  24. /// A structure to hold the key and the values associated with the key.
  25. /// The number of values must always be 1 or greater in a version that is
  26. /// stored, but can be zero in a dummy version used only for lookups.
  27. /// </summary>
  28. private struct KeyAndValues
  29. {
  30. #region Copy (Static)
  31. /// <summary>
  32. /// Make a copy of a KeyAndValues, copying the array.
  33. /// </summary>
  34. /// <param name="x">KeyAndValues to copy.</param>
  35. /// <returns>A copied version.</returns>
  36. public static KeyAndValues Copy(KeyAndValues x)
  37. {
  38. KeyAndValues result;
  39. result.Key = x.Key;
  40. result.Count = x.Count;
  41. if (x.Values != null)
  42. {
  43. result.Values = (TValue[])x.Values.Clone();
  44. }
  45. else
  46. {
  47. result.Values = null;
  48. }
  49. return result;
  50. }
  51. #endregion
  52. #region Key (Public)
  53. /// <summary>
  54. /// The key.
  55. /// </summary>
  56. public TKey Key;
  57. #endregion
  58. #region Count (Public)
  59. /// <summary>
  60. /// The number of values. Always at least 1 except in a dummy version
  61. /// for lookups.
  62. /// </summary>
  63. public int Count;
  64. #endregion
  65. #region Values (Public)
  66. /// <summary>
  67. /// An array of values.
  68. /// </summary>
  69. public TValue[] Values;
  70. #endregion
  71. #region Constructors
  72. /// <summary>
  73. /// Create a dummy KeyAndValues with just the key, for lookups.
  74. /// </summary>
  75. /// <param name="key">The key to use.</param>
  76. public KeyAndValues(TKey key)
  77. {
  78. Key = key;
  79. Count = 0;
  80. Values = null;
  81. }
  82. #endregion
  83. }
  84. #endregion
  85. #region KeyAndValuesEqualityComparer Class
  86. /// <summary>
  87. /// This class implements IEqualityComparer for KeysAndValues, allowing
  88. /// them to be compared by their keys. An IEqualityComparer on keys is
  89. /// required.
  90. /// </summary>
  91. private class KeyAndValuesEqualityComparer : IEqualityComparer<KeyAndValues>
  92. {
  93. #region Private
  94. #region keyEqualityComparer (Private)
  95. /// <summary>
  96. /// Key equality comparer
  97. /// </summary>
  98. private readonly IEqualityComparer<TKey> keyEqualityComparer;
  99. #endregion
  100. #endregion
  101. #region Constructors
  102. /// <summary>
  103. /// Create key and values equality comparer
  104. /// </summary>
  105. /// <param name="keyEqualityComparer">Key equality comparer</param>
  106. public KeyAndValuesEqualityComparer(
  107. IEqualityComparer<TKey> keyEqualityComparer)
  108. {
  109. this.keyEqualityComparer = keyEqualityComparer;
  110. }
  111. #endregion
  112. #region IEqualityComparer<MultiDictionary<TKey,TValue>.KeyAndValues> Members
  113. /// <summary>
  114. /// Equals
  115. /// </summary>
  116. /// <param name="x">X value</param>
  117. /// <param name="y">Y value</param>
  118. /// <returns>True if x and y match.</returns>
  119. public bool Equals(KeyAndValues x, KeyAndValues y)
  120. {
  121. return keyEqualityComparer.Equals(x.Key, y.Key);
  122. }
  123. /// <summary>
  124. /// Get hash code
  125. /// </summary>
  126. /// <param name="obj">Object to generate hash code for</param>
  127. /// <returns>Hash code</returns>
  128. public int GetHashCode(KeyAndValues obj)
  129. {
  130. return obj.Key == null
  131. ? 0x1786E23C
  132. : keyEqualityComparer.GetHashCode(obj.Key);
  133. }
  134. #endregion
  135. }
  136. #endregion
  137. #region GetHashCode (Static)
  138. /// <summary>
  139. /// Gets the hash code for an object using a comparer. Correctly handles
  140. /// null.
  141. /// </summary>
  142. /// <param name="item">Item to get hash code for. Can be null.</param>
  143. /// <param name="equalityComparer">The comparer to use.</param>
  144. /// <returns>The hash code for the item.</returns>
  145. public static int GetHashCode<T>(T item,
  146. IEqualityComparer<T> equalityComparer)
  147. {
  148. if (item == null)
  149. {
  150. return 0x1786E23C;
  151. }
  152. else
  153. {
  154. return equalityComparer.GetHashCode(item);
  155. }
  156. }
  157. #endregion
  158. #region IsCloneableType (Static)
  159. /// <summary>
  160. /// Determine if a type is cloneable: either a value type or implementing
  161. /// ICloneable.
  162. /// </summary>
  163. /// <param name="type">Type to check.</param>
  164. /// <param name="isValue">Returns if the type is a value type, and does not
  165. /// implement ICloneable.</param>
  166. /// <returns>True if the type is cloneable.</returns>
  167. public static bool IsCloneableType(Type type, out bool isValue)
  168. {
  169. isValue = false;
  170. if (typeof(ICloneable<MultiDictionary<TKey, TValue>>).
  171. IsAssignableFrom(type))
  172. {
  173. return true;
  174. }
  175. else if (type.IsValueType)
  176. {
  177. isValue = true;
  178. return true;
  179. }
  180. else
  181. {
  182. return false;
  183. }
  184. }
  185. #endregion
  186. #region KeyComparer (Public)
  187. /// <summary>
  188. /// Returns the IEqualityComparer&lt;T&gt; used to compare keys in this
  189. /// dictionary.
  190. /// </summary>
  191. /// <value>If the dictionary was created using a comparer, that comparer
  192. /// is returned. Otherwise the default comparer for TKey
  193. /// (EqualityComparer&lt;TKey&gt;.Default) is returned.</value>
  194. /// <typeparam name="TKey">TKey</typeparam>
  195. /// <typeparam name="TValue">TValue</typeparam>
  196. public IEqualityComparer<TKey> KeyComparer
  197. {
  198. get
  199. {
  200. return keyEqualityComparer;
  201. }
  202. }
  203. #endregion
  204. #region ValueComparer (Public)
  205. /// <summary>
  206. /// Returns the IEqualityComparer&lt;T&gt; used to compare values in this
  207. /// dictionary.
  208. /// </summary>
  209. /// <value>If the dictionary was created using a comparer, that comparer
  210. /// is returned. Otherwise the default comparer for TValue
  211. /// (EqualityComparer&lt;TValue&gt;.Default) is returned.</value>
  212. /// <typeparam name="TKey">TKey</typeparam>
  213. /// <typeparam name="TValue">TValue</typeparam>
  214. public IEqualityComparer<TValue> ValueComparer
  215. {
  216. get
  217. {
  218. return valueEqualityComparer;
  219. }
  220. }
  221. #endregion
  222. #region Count (Public)
  223. /// <summary>
  224. /// Gets the number of key-value pairs in the dictionary. Each value
  225. /// associated with a given key is counted. If duplicate values are
  226. /// permitted, each duplicate value is included in the count.
  227. /// </summary>
  228. /// <value>The number of key-value pairs in the dictionary.</value>
  229. /// <typeparam name="TKey">TKey</typeparam>
  230. /// <typeparam name="TValue">TValue</typeparam>
  231. public override sealed int Count
  232. {
  233. get
  234. {
  235. return hash.ElementCount;
  236. }
  237. }
  238. #endregion
  239. #region Private
  240. #region keyEqualityComparer (Private)
  241. /// <summary>
  242. /// The comparer for comparing keys
  243. /// </summary>
  244. /// <typeparam name="TKey">TKey</typeparam>
  245. /// <typeparam name="TValue">TValue</typeparam>
  246. private readonly IEqualityComparer<TKey> keyEqualityComparer;
  247. #endregion
  248. #region valueEqualityComparer (Private)
  249. /// <summary>
  250. /// The comparer for comparing values;
  251. /// </summary>
  252. /// <typeparam name="TKey">TKey</typeparam>
  253. /// <typeparam name="TValue">TValue</typeparam>
  254. private readonly IEqualityComparer<TValue> valueEqualityComparer;
  255. #endregion
  256. #region equalityComparer (Private)
  257. /// <summary>
  258. /// The comparer for compaing keys and values.
  259. /// </summary>
  260. /// <typeparam name="TKey">TKey</typeparam>
  261. /// <typeparam name="TValue">TValue</typeparam>
  262. private readonly IEqualityComparer<KeyAndValues> equalityComparer;
  263. #endregion
  264. #region allowDuplicateValues (Private)
  265. /// <summary>
  266. /// Whether duplicate values for the same key are allowed.
  267. /// </summary>
  268. /// <typeparam name="TKey">TKey</typeparam>
  269. /// <typeparam name="TValue">TValue</typeparam>
  270. private readonly bool allowDuplicateValues;
  271. #endregion
  272. #region hash (Private)
  273. /// <summary>
  274. /// The hash that holds the keys and values.
  275. /// </summary>
  276. /// <typeparam name="TKey">TKey</typeparam>
  277. /// <typeparam name="TValue">TValue</typeparam>
  278. private Hash<KeyAndValues> hash;
  279. #endregion
  280. #endregion
  281. #region Constructors
  282. /// <summary>
  283. /// Create a new MultiDictionary. The default ordering of keys and values
  284. /// are used. If duplicate values are allowed, multiple copies of the same
  285. /// value can be associated with the same key. For example, the key "foo"
  286. /// could have "a", "a", and "b" associated with it. If duplicate values
  287. /// are not allowed, only one copies of a given value can be associated
  288. /// with the same key, although different keys can have the same value.
  289. /// For example, the key "foo" could have "a" and "b" associated with it,
  290. /// which key "bar" has values "b" and "c" associated with it.
  291. /// </summary>
  292. /// <remarks>The default ordering of keys and values will be used, as
  293. /// defined by TKey and TValue's implementation of IComparable&lt;T&gt;
  294. /// (or IComparable if IComparable&lt;T&gt; is not implemented). If a
  295. /// different ordering should be used, other constructors allow a custom
  296. /// Comparer or IComparer to be passed to changed the ordering.</remarks>
  297. /// <param name="allowDuplicateValues">Can the same value be associated
  298. /// with a key multiple times?</param>
  299. /// <exception cref="InvalidOperationException">TKey or TValue does not
  300. /// implement either IComparable&lt;T&gt; or IComparable.</exception>
  301. public MultiDictionary(bool allowDuplicateValues)
  302. : this(allowDuplicateValues,
  303. EqualityComparer<TKey>.Default,
  304. EqualityComparer<TValue>.Default)
  305. {
  306. }
  307. /// <summary>
  308. /// Create a new MultiDictionary. If duplicate values are allowed,
  309. /// multiple copies of the same value can be associated with the same key.
  310. /// For example, the key "foo" could have "a", "a", and "b" associated
  311. /// with it. If duplicate values are not allowed, only one copies of a
  312. /// given value can be associated with the same key, although different
  313. /// keys can have the same value. For example, the key "foo" could have
  314. /// "a" and "b" associated with it, which key "bar" has values "b" and "c"
  315. /// associated with it.
  316. /// </summary>
  317. /// <param name="allowDuplicateValues">
  318. /// Can the same value be associated with a key multiple times?</param>
  319. /// <param name="keyEqualityComparer">
  320. /// An IEqualityComparer&lt;TKey&gt; instance that will be used to compare
  321. /// keys.</param>
  322. /// <exception cref="InvalidOperationException">TValue does not implement
  323. /// either IComparable&lt;TValue&gt; or IComparable.</exception>
  324. public MultiDictionary(bool allowDuplicateValues,
  325. IEqualityComparer<TKey> keyEqualityComparer)
  326. : this(allowDuplicateValues, keyEqualityComparer,
  327. EqualityComparer<TValue>.Default)
  328. {
  329. }
  330. /// <summary>
  331. /// Create a new MultiDictionary. If duplicate values are allowed,
  332. /// multiple copies of the same value can be associated with the same key.
  333. /// For example, the key "foo" could have "a", "a", and "b" associated
  334. /// with it. If duplicate values are not allowed, only one copies of a
  335. /// given value can be associated with the same key, although different
  336. /// keys can have the same value. For example, the key "foo" could have
  337. /// "a" and "b" associated with it, which key "bar" has values "b" and "c"
  338. /// associated with it.
  339. /// </summary>
  340. /// <param name="allowDuplicateValues">Can the same value be associated
  341. /// with a key multiple times?</param>
  342. /// <param name="keyEqualityComparer">An IEqualityComparer&lt;TKey&gt;
  343. /// instance that will be used to compare keys.</param>
  344. /// <param name="valueEqualityComparer">An IEqualityComparer&lt;TValue&gt;
  345. /// instance that will be used to compare values.</param>
  346. public MultiDictionary(bool allowDuplicateValues,
  347. IEqualityComparer<TKey> keyEqualityComparer,
  348. IEqualityComparer<TValue> valueEqualityComparer)
  349. {
  350. if (keyEqualityComparer == null)
  351. {
  352. throw new ArgumentNullException("keyEqualityComparer");
  353. }
  354. if (valueEqualityComparer == null)
  355. {
  356. throw new ArgumentNullException("valueEqualityComparer");
  357. }
  358. this.allowDuplicateValues = allowDuplicateValues;
  359. this.keyEqualityComparer = keyEqualityComparer;
  360. this.valueEqualityComparer = valueEqualityComparer;
  361. equalityComparer =
  362. new KeyAndValuesEqualityComparer(keyEqualityComparer);
  363. hash = new Hash<KeyAndValues>(equalityComparer);
  364. }
  365. /// <summary>
  366. /// Create a new MultiDictionary. Private constructor, for use by Clone().
  367. /// </summary>
  368. /// <param name="allowDuplicateValues">Allow duplicate values</param>
  369. /// <param name="equalityComparer">Equality comparer</param>
  370. /// <param name="hash">hash</param>
  371. /// <param name="keyEqualityComparer">Key equality comparer</param>
  372. /// <param name="valueEqualityComparer">Value equality comparer</param>
  373. private MultiDictionary(bool allowDuplicateValues,
  374. IEqualityComparer<TKey> keyEqualityComparer,
  375. IEqualityComparer<TValue> valueEqualityComparer,
  376. IEqualityComparer<KeyAndValues> equalityComparer,
  377. Hash<KeyAndValues> hash)
  378. {
  379. if (keyEqualityComparer == null)
  380. {
  381. throw new ArgumentNullException("keyEqualityComparer");
  382. }
  383. if (valueEqualityComparer == null)
  384. {
  385. throw new ArgumentNullException("valueEqualityComparer");
  386. }
  387. this.allowDuplicateValues = allowDuplicateValues;
  388. this.keyEqualityComparer = keyEqualityComparer;
  389. this.valueEqualityComparer = valueEqualityComparer;
  390. this.equalityComparer = equalityComparer;
  391. this.hash = hash;
  392. }
  393. #endregion
  394. #region ICloneable<MultiDictionary<TKey,TValue>> Members
  395. /// <summary>
  396. /// Makes a shallow clone of this dictionary; i.e., if keys or values of
  397. /// the dictionary are reference types, then they are not cloned. If TKey
  398. /// or TValue is a value type, then each element is copied as if by simple
  399. /// assignment.
  400. /// </summary>
  401. /// <remarks>Cloning the dictionary takes time O(N), where N is the number
  402. /// of key-value pairs in the dictionary.</remarks>
  403. /// <returns>The cloned dictionary.</returns>
  404. public MultiDictionary<TKey, TValue> Clone()
  405. {
  406. return new MultiDictionary<TKey, TValue>(
  407. allowDuplicateValues, keyEqualityComparer,
  408. valueEqualityComparer, equalityComparer,
  409. hash.Clone(KeyAndValues.Copy));
  410. }
  411. #endregion
  412. #region Add (Public)
  413. /// <summary>
  414. /// <para>Adds a new value to be associated with a key. If duplicate
  415. /// values are permitted, this method always adds a new key-value pair
  416. /// to the dictionary.</para>
  417. /// <para>If duplicate values are not permitted, and <paramref name="key"/>
  418. /// already has a value equal to <paramref name="value"/> associated with
  419. /// it, then that value is replaced with <paramref name="value"/>,
  420. /// and the number of values associate with <paramref name="key"/> is
  421. /// unchanged.</para>
  422. /// </summary>
  423. /// <param name="key">Key</param>
  424. /// <param name="value">Value</param>
  425. public override sealed void Add(TKey key, TValue value)
  426. {
  427. KeyAndValues keyValues = new KeyAndValues(key);
  428. KeyAndValues existing;
  429. if (hash.Find(keyValues, false, out existing))
  430. {
  431. // There already is an item in the hash table equal to this key.
  432. // Add the new value, taking into account duplicates if needed.
  433. int existingCount = existing.Count;
  434. if (!allowDuplicateValues)
  435. {
  436. int valueHash = GetHashCode(value, valueEqualityComparer);
  437. for (int i = 0; i < existingCount; ++i)
  438. {
  439. if (GetHashCode(existing.Values[i], valueEqualityComparer) ==
  440. valueHash &&
  441. valueEqualityComparer.Equals(existing.Values[i], value))
  442. {
  443. // Found an equal existing value. Replace it and we're done.
  444. existing.Values[i] = value;
  445. return;
  446. }
  447. }
  448. }
  449. // Add a new value to an existing key.
  450. if (existingCount == existing.Values.Length)
  451. {
  452. // Grow the array to make room.
  453. TValue[] newValues = new TValue[existingCount * 2];
  454. Array.Copy(existing.Values, newValues, existingCount);
  455. existing.Values = newValues;
  456. }
  457. existing.Values[existingCount] = value;
  458. existing.Count = existingCount + 1;
  459. // Update the hash table.
  460. hash.Find(existing, true, out keyValues);
  461. return;
  462. }
  463. else
  464. {
  465. // No item with this key. Add it.
  466. keyValues.Count = 1;
  467. keyValues.Values = new TValue[1]
  468. {
  469. value
  470. };
  471. hash.Insert(keyValues, true, out existing);
  472. return;
  473. }
  474. }
  475. #endregion
  476. #region Remove (Public)
  477. /// <summary>
  478. /// Removes a given value from the values associated with a key. If the
  479. /// last value is removed from a key, the key is removed also.
  480. /// </summary>
  481. /// <param name="key">A key to remove a value from.</param>
  482. /// <param name="value">The value to remove.</param>
  483. /// <returns>True if <paramref name="value"/> was associated with
  484. /// <paramref name="key"/> (and was therefore removed). False if
  485. /// <paramref name="value"/> was not associated with
  486. /// <paramref name="key"/>.</returns>
  487. public override sealed bool Remove(TKey key, TValue value)
  488. {
  489. KeyAndValues keyValues = new KeyAndValues(key);
  490. KeyAndValues existing;
  491. if (hash.Find(keyValues, false, out existing))
  492. {
  493. // There is an item in the hash table equal to this key.
  494. // Find the value.
  495. int existingCount = existing.Count;
  496. int valueHash = GetHashCode(value, valueEqualityComparer);
  497. int indexFound = -1;
  498. for (int i = 0; i < existingCount; ++i)
  499. {
  500. if (GetHashCode(existing.Values[i], valueEqualityComparer) ==
  501. valueHash &&
  502. valueEqualityComparer.Equals(existing.Values[i], value))
  503. {
  504. // Found an equal existing value
  505. indexFound = i;
  506. }
  507. }
  508. if (existingCount == 1)
  509. {
  510. // Removing the last value. Remove the key.
  511. hash.Delete(existing, out keyValues);
  512. return true;
  513. }
  514. else if (indexFound >= 0)
  515. {
  516. // Found a value. Remove it.
  517. if (indexFound < existingCount - 1)
  518. {
  519. Array.Copy(existing.Values, indexFound + 1, existing.Values,
  520. indexFound, existingCount - indexFound - 1);
  521. }
  522. existing.Count = existingCount - 1;
  523. // Update the hash.
  524. hash.Find(existing, true, out keyValues);
  525. return true;
  526. }
  527. else
  528. {
  529. // Value was not found.
  530. return false;
  531. }
  532. }
  533. else
  534. {
  535. return false; // key not found.
  536. }
  537. }
  538. /// <summary>
  539. /// Removes a key and all associated values from the dictionary. If the
  540. /// key is not present in the dictionary, it is unchanged and false is
  541. /// returned.
  542. /// </summary>
  543. /// <param name="key">The key to remove.</param>
  544. /// <returns>True if the key was present and was removed. Returns
  545. /// false if the key was not present.</returns>
  546. public override sealed bool Remove(TKey key)
  547. {
  548. KeyAndValues dummy;
  549. return hash.Delete(new KeyAndValues(key), out dummy);
  550. }
  551. #endregion
  552. #region Clear (Public)
  553. /// <summary>
  554. /// Removes all keys and values from the dictionary.
  555. /// </summary>
  556. public override sealed void Clear()
  557. {
  558. hash.StopEnumerations(); // Invalidate any enumerations.
  559. // The simplest and fastest way is simply to throw away the old hash
  560. // and create a new one.
  561. hash = new Hash<KeyAndValues>(equalityComparer);
  562. }
  563. #endregion
  564. #region Contains (Public)
  565. /// <summary>
  566. /// Checks to see if <paramref name="value"/> is associated with
  567. /// <paramref name="key"/> in the dictionary.
  568. /// </summary>
  569. /// <param name="key">The key to check.</param>
  570. /// <param name="value">The value to check.</param>
  571. /// <returns>True if <paramref name="value"/> is associated with
  572. /// <paramref name="key"/>.</returns>
  573. public override sealed bool Contains(TKey key, TValue value)
  574. {
  575. KeyAndValues find = new KeyAndValues(key);
  576. KeyAndValues item;
  577. if (hash.Find(find, false, out item))
  578. {
  579. int existingCount = item.Count;
  580. int valueHash = GetHashCode(value, valueEqualityComparer);
  581. for (int i = 0; i < existingCount; ++i)
  582. {
  583. if (GetHashCode(item.Values[i], valueEqualityComparer) ==
  584. valueHash &&
  585. valueEqualityComparer.Equals(item.Values[i], value))
  586. {
  587. // Found an equal existing value.
  588. return true;
  589. }
  590. }
  591. }
  592. return false;
  593. }
  594. #endregion
  595. #region ContainsKey (Public)
  596. /// <summary>
  597. /// Checks to see if the key is present in the dictionary and has
  598. /// at least one value associated with it.
  599. /// </summary>
  600. /// <param name="key">The key to check.</param>
  601. /// <returns>True if <paramref name="key"/> is present and has at least
  602. /// one value associated with it. Returns false otherwise.</returns>
  603. public override sealed bool ContainsKey(TKey key)
  604. {
  605. KeyAndValues find = new KeyAndValues(key);
  606. KeyAndValues temp;
  607. return hash.Find(find, false, out temp);
  608. }
  609. #endregion
  610. #region CloneContents (Public)
  611. /// <summary>
  612. /// Makes a deep clone of this dictionary. A new dictionary is created
  613. /// with a clone of each entry of this dictionary, by calling
  614. /// ICloneable.Clone on each element. If TKey or TValue is a value type,
  615. /// then each element is copied as if by simple assignment.
  616. /// </summary>
  617. /// <remarks><para>If TKey or TValue is a reference type, it must implement
  618. /// ICloneable. Otherwise, an InvalidOperationException is thrown.</para>
  619. /// <para>Cloning the dictionary takes time O(N log N), where N is the
  620. /// number of key-value pairs in the dictionary.</para></remarks>
  621. /// <returns>The cloned dictionary.</returns>
  622. /// <exception cref="InvalidOperationException">TKey or TValue is a
  623. /// reference type that does not implement ICloneable.</exception>
  624. public MultiDictionary<TKey, TValue> CloneContents()
  625. {
  626. bool keyIsValueType, valueIsValueType;
  627. // Make sure that TKey and TValue can be cloned.
  628. if (!IsCloneableType(typeof(TKey), out keyIsValueType))
  629. {
  630. NonCloneableType(typeof(TKey));
  631. }
  632. if (!IsCloneableType(typeof(TValue), out valueIsValueType))
  633. {
  634. NonCloneableType(typeof(TValue));
  635. }
  636. // It's tempting to do a more efficient cloning, utilizing the
  637. // hash.Clone() method. However, we can't know that
  638. // the cloned version of the key has the same hash value.
  639. MultiDictionary<TKey, TValue> newDict =
  640. new MultiDictionary<TKey, TValue>(
  641. allowDuplicateValues, keyEqualityComparer, valueEqualityComparer);
  642. foreach (KeyAndValues item in hash)
  643. {
  644. // Clone the key and values parts. Value types can be cloned
  645. // by just copying them, otherwise, ICloneable is used.
  646. TKey keyClone;
  647. TValue[] valuesClone;
  648. if (keyIsValueType)
  649. {
  650. keyClone = item.Key;
  651. }
  652. else
  653. {
  654. if (item.Key == null)
  655. {
  656. // Really null, because we know TKey isn't a value type.
  657. keyClone = default(TKey);
  658. }
  659. else
  660. {
  661. keyClone = ((ICloneable<TKey>)item.Key).Clone();
  662. }
  663. }
  664. valuesClone = new TValue[item.Count];
  665. if (valueIsValueType)
  666. {
  667. Array.Copy(item.Values, valuesClone, item.Count);
  668. }
  669. else
  670. {
  671. for (int i = 0; i < item.Count; ++i)
  672. {
  673. if (item.Values[i] == null)
  674. {
  675. // Really null, because we know TKey isn't a value type.
  676. valuesClone[i] = default(TValue);
  677. }
  678. else
  679. {
  680. valuesClone[i] = ((ICloneable<TValue>)item.Values[i]).Clone();
  681. }
  682. }
  683. }
  684. newDict.AddMany(keyClone, valuesClone);
  685. }
  686. return newDict;
  687. }
  688. #endregion
  689. #region Methods (Private)
  690. #region EqualValues
  691. /// <summary>
  692. /// Determine if two values are equal.
  693. /// </summary>
  694. /// <param name="value1">First value to compare.</param>
  695. /// <param name="value2">Second value to compare.</param>
  696. /// <returns>True if the values are equal.</returns>
  697. protected override sealed bool EqualValues(TValue value1, TValue value2)
  698. {
  699. return valueEqualityComparer.Equals(value1, value2);
  700. }
  701. #endregion
  702. #region EnumerateKeys
  703. /// <summary>
  704. /// Enumerate all the keys in the dictionary.
  705. /// </summary>
  706. /// <returns>
  707. /// An IEnumerator&lt;TKey&gt; that enumerates all of the keys in the
  708. /// dictionary that have at least one value associated with them.
  709. /// </returns>
  710. protected override sealed IEnumerator<TKey> EnumerateKeys()
  711. {
  712. foreach (KeyAndValues item in hash)
  713. {
  714. yield return item.Key;
  715. }
  716. }
  717. #endregion
  718. #region EnumerateValues
  719. /// <summary>
  720. /// Enumerate the values in the a KeyAndValues structure. Can't return
  721. /// the array directly because:
  722. /// a) The array might be larger than the count.
  723. /// b) We can't allow clients to down-cast to the array and modify it.
  724. /// c) We have to abort enumeration if the hash changes.
  725. /// </summary>
  726. /// <param name="keyAndValues">Item with the values to enumerate..</param>
  727. /// <returns>An enumerable that enumerates the items in the KeyAndValues
  728. /// structure.</returns>
  729. private IEnumerator<TValue> EnumerateValues(KeyAndValues keyAndValues)
  730. {
  731. int count = keyAndValues.Count;
  732. int stamp = hash.GetEnumerationStamp();
  733. for (int i = 0; i < count; ++i)
  734. {
  735. yield return keyAndValues.Values[i];
  736. hash.CheckEnumerationStamp(stamp);
  737. }
  738. }
  739. #endregion
  740. #region TryEnumerateValuesForKey
  741. /// <summary>
  742. /// Determines if this dictionary contains a key equal to
  743. /// <paramref name="key"/>. If so, all the values associated with that key
  744. /// are returned through the values parameter.
  745. /// </summary>
  746. /// <param name="key">The key to search for.</param>
  747. /// <param name="values">Returns all values associated with key, if true
  748. /// was returned.</param>
  749. /// <returns>True if the dictionary contains key. False if the dictionary
  750. /// does not contain key.</returns>
  751. protected override sealed bool TryEnumerateValuesForKey(
  752. TKey key, out IEnumerator<TValue> values)
  753. {
  754. KeyAndValues find = new KeyAndValues(key);
  755. KeyAndValues item;
  756. if (hash.Find(find, false, out item))
  757. {
  758. values = EnumerateValues(item);
  759. return true;
  760. }
  761. else
  762. {
  763. values = null;
  764. return false;
  765. }
  766. }
  767. #endregion
  768. #region CountValues
  769. /// <summary>
  770. /// Gets the number of values associated with a given key.
  771. /// </summary>
  772. /// <param name="key">The key to count values of.</param>
  773. /// <returns>The number of values associated with <paramref name="key"/>.
  774. /// If <paramref name="key"/> is not present in the dictionary, zero is
  775. /// returned.</returns>
  776. protected override sealed int CountValues(TKey key)
  777. {
  778. KeyAndValues find = new KeyAndValues(key);
  779. KeyAndValues item;
  780. if (hash.Find(find, false, out item))
  781. {
  782. return item.Count;
  783. }
  784. else
  785. {
  786. return 0;
  787. }
  788. }
  789. #endregion
  790. #region NonCloneableType
  791. /// <summary>
  792. /// Throw an InvalidOperationException indicating that this type is not
  793. /// cloneable.
  794. /// </summary>
  795. /// <param name="t">Type to test.</param>
  796. private void NonCloneableType(Type t)
  797. {
  798. throw new InvalidOperationException(
  799. "Type " + t.FullName + " does not implement ICloneable.");
  800. }
  801. #endregion
  802. #endregion
  803. }
  804. /// <summary>
  805. /// Multi dictionary tests, needs to be an extra class because
  806. /// MultiDictionary is generic.
  807. /// </summary>
  808. internal class MultiDictionaryTests
  809. {
  810. #region Helpers
  811. #region CheckMultiDictionaryContents
  812. /// <summary>
  813. /// Check the contents of a Multi-Dictionary non-destructively.
  814. /// Keys and Values must be in order.
  815. /// </summary>
  816. /// <param name="dict">Dictionary</param>
  817. /// <param name="keys">Keys</param>
  818. /// <param name="values">Values</param>
  819. /// <param name="nonKey">Non key</param>
  820. /// <param name="nonValue">Non value</param>
  821. internal static void CheckMultiDictionaryContents<TKey, TValue>(
  822. MultiDictionary<TKey, TValue> dict, TKey[] keys, TValue[][] values,
  823. TKey nonKey, TValue nonValue)
  824. {
  825. //Optimize performance of this test helper method, it is still to slow
  826. int iKey, iValue;
  827. ICollection<TValue> getValues;
  828. // Check Count.
  829. Assert.Equal(keys.Length, dict.Count);
  830. // Check indexer, ContainsKey, Contains, TryGetValue for each key.
  831. for (iKey = 0; iKey < keys.Length; ++iKey)
  832. {
  833. Assert.True(dict.ContainsKey(keys[iKey]));
  834. Assert.True(dict.Contains(
  835. new KeyValuePair<TKey, ICollection<TValue>>(
  836. keys[iKey], values[iKey])));
  837. bool b = ((IDictionary<TKey, ICollection<TValue>>)dict).TryGetValue(
  838. keys[iKey], out getValues);
  839. Assert.True(b);
  840. iValue = 0;
  841. CollectionBaseTests.CheckEnumerableElementsAnyOrder(
  842. getValues, values[iKey]);
  843. iValue = 0;
  844. foreach (TValue val in values[iKey])
  845. {
  846. Assert.True(dict.Contains(keys[iKey], val));
  847. ++iValue;
  848. } // foreach
  849. Assert.True(iValue == values[iKey].Length);
  850. iValue = 0;
  851. getValues = dict[keys[iKey]];
  852. CollectionBaseTests.CheckEnumerableElementsAnyOrder(
  853. getValues, values[iKey]);
  854. } // for (iKey)
  855. // Check Keys collection.
  856. CollectionBaseTests.CompareCollections(
  857. dict.Keys, keys, false);
  858. // Check Values collection
  859. int a = 0;
  860. TValue[] vals = new TValue[dict.Values.Count];
  861. for (iKey = 0; iKey < keys.Length; ++iKey)
  862. {
  863. for (iValue = 0; iValue < values[iKey].Length; ++iValue)
  864. {
  865. vals[a++] = values[iKey][iValue];
  866. } // for (iValue)
  867. } // for (iKey)
  868. Assert.Equal(dict.Values.Count, a);
  869. CollectionBaseTests.CompareCollections(
  870. dict.Values, vals, false);
  871. // Check KeyValuePairs collection.
  872. a = 0;
  873. KeyValuePair<TKey, TValue>[] pairs =
  874. new KeyValuePair<TKey, TValue>[dict.Values.Count];
  875. for (iKey = 0; iKey < keys.Length; ++iKey)
  876. {
  877. for (iValue = 0; iValue < values[iKey].Length; ++iValue)
  878. {
  879. pairs[a++] = new KeyValuePair<TKey, TValue>(
  880. keys[iKey], values[iKey][iValue]);
  881. } // for (iValue)
  882. } // for (iKey)
  883. CollectionBaseTests.CompareCollections(
  884. dict.KeyValuePairs, pairs, false);
  885. // Tests Contains, ContainsKey, TryGetValue for wrong values.
  886. Assert.False(dict.ContainsKey(nonKey));
  887. Assert.False(((IDictionary<TKey, ICollection<TValue>>)dict).
  888. TryGetValue(nonKey, out getValues));
  889. for (iKey = 0; iKey < keys.Length; ++iKey)
  890. {
  891. Assert.False(dict.Contains(keys[iKey], nonValue));
  892. Assert.False(dict.Contains(
  893. new KeyValuePair<TKey, ICollection<TValue>>(
  894. keys[iKey], new TValue[1] { nonValue })));
  895. }
  896. }
  897. #endregion
  898. #region FirstLetterComparer
  899. /// <summary>
  900. /// First letter comparer
  901. /// </summary>
  902. private class FirstLetterComparer : IEqualityComparer<string>
  903. {
  904. #region IEqualityComparer<string> Members
  905. /// <summary>
  906. /// Equals
  907. /// </summary>
  908. /// <param name="x">X value</param>
  909. /// <param name="y">Y value</param>
  910. /// <returns>True if both x and y match</returns>
  911. public bool Equals(string x, string y)
  912. {
  913. if (x == null)
  914. {
  915. return y == null;
  916. }
  917. else if (x.Length == 0)
  918. {
  919. return (y != null && y.Length == 0);
  920. }
  921. else
  922. {
  923. if (y == null ||
  924. y.Length == 0)
  925. {
  926. return false;
  927. }
  928. else
  929. {
  930. return x[0] == y[0];
  931. }
  932. }
  933. }
  934. /// <summary>
  935. /// Get hash code
  936. /// </summary>
  937. /// <param name="obj">Object</param>
  938. /// <returns>Int</returns>
  939. public int GetHashCode(string obj)
  940. {
  941. if (obj == null)
  942. {
  943. return 0x12383;
  944. }
  945. else if (obj.Length == 0)
  946. {
  947. return 17;
  948. }
  949. else
  950. {
  951. return obj[0].GetHashCode();
  952. }
  953. }
  954. #endregion
  955. }
  956. #endregion
  957. #region CompareClones
  958. /// <summary>
  959. /// Compare clones
  960. /// </summary>
  961. /// <param name="d1">D 1</param>
  962. /// <param name="d2">D 2</param>
  963. private void CompareClones<K, V>(
  964. MultiDictionary<K, V> d1, MultiDictionary<K, V> d2)
  965. {
  966. IEnumerable<KeyValuePair<K, V>> e1 = d1.KeyValuePairs;
  967. IEnumerable<KeyValuePair<K, V>> e2 = d2.KeyValuePairs;
  968. KeyValuePair<K, V>[]
  969. pairs1 = ArrayHelper.ToArray(e1),
  970. pairs2 = ArrayHelper.ToArray(e2);
  971. bool[] found = new bool[pairs2.Length];
  972. // Check that the arrays are equal, but not reference equals
  973. // (e.g., have been cloned).
  974. Assert.True(pairs1.Length == pairs2.Length);
  975. foreach (KeyValuePair<K, V> p1 in pairs1)
  976. {
  977. bool f = false;
  978. for (int i = 0; i < pairs2.Length; ++i)
  979. {
  980. if (!found[i] &&
  981. Equals(p1.Key, pairs2[i].Key) &&
  982. Equals(p1.Value, pairs2[i].Value))
  983. {
  984. found[i] = true;
  985. f = true;
  986. Assert.True(p1.Key == null ||
  987. !ReferenceEquals(p1.Key, pairs2[i].Key));
  988. Assert.True(p1.Value == null ||
  989. !ReferenceEquals(p1.Value, pairs2[i].Value));
  990. break;
  991. }
  992. }
  993. Assert.True(f);
  994. }
  995. }
  996. #endregion
  997. #endregion
  998. #region Clone (LongRunning)
  999. /// <summary>
  1000. /// Clone. Note: Too slow for a dynamic unit test.
  1001. /// </summary>
  1002. [Test, Category("LongRunning")]
  1003. public static void Clone()
  1004. {
  1005. IEqualityComparer<string> firstLetterComparer =
  1006. new FirstLetterComparer();
  1007. MultiDictionary<string, string> dict1 =
  1008. new MultiDictionary<string, string>(false,
  1009. StringComparer.InvariantCultureIgnoreCase,
  1010. firstLetterComparer);
  1011. dict1.Add("qubert", "dinosaur");
  1012. dict1.Add("Hello", "AAA");
  1013. dict1.Add("Hi", "aaa");
  1014. dict1.Add("Qubert", "hello");
  1015. dict1.Add("queztel", "hello");
  1016. dict1.Add("Alpha", "omega");
  1017. dict1.Add("alpha", "oz");
  1018. dict1.Add("qubert", "hippy");
  1019. MultiDictionary<string, string> dict2 = dict1.Clone();
  1020. Assert.True(dict1 != dict2);
  1021. dict2.Add("qubert", "hoover");
  1022. dict2.Remove("queztel");
  1023. dict2.Add("hello", "banana");
  1024. CollectionBaseTests.CheckEnumerableElementsAnyOrder(
  1025. dict1.KeyValuePairs, new[]
  1026. {
  1027. new KeyValuePair<string, string>("Alpha", "oz"),
  1028. new KeyValuePair<string, string>("Hello", "AAA"),
  1029. new KeyValuePair<string, string>("Hi", "aaa"),
  1030. new KeyValuePair<string, string>("qubert", "hippy"),
  1031. new KeyValuePair<string, string>("qubert", "dinosaur"),
  1032. new KeyValuePair<string, string>("queztel", "hello")
  1033. });
  1034. CollectionBaseTests.CheckEnumerableElementsAnyOrder(
  1035. dict2.KeyValuePairs, new[]
  1036. {
  1037. new KeyValuePair<string, string>("Alpha", "oz"),
  1038. new KeyValuePair<string, string>("Hello", "banana"),
  1039. new KeyValuePair<string, string>("Hello", "AAA"),
  1040. new KeyValuePair<string, string>("Hi", "aaa"),
  1041. new KeyValuePair<string, string>("qubert", "hoover"),
  1042. new KeyValuePair<string, string>("qubert", "dinosaur")
  1043. });
  1044. dict2 = dict1.Clone();
  1045. Assert.True(dict1 != dict2);
  1046. dict2.Add("qubert", "hoover");
  1047. dict2.Remove("queztel");
  1048. dict2.Add("hello", "banana");
  1049. CollectionBaseTests.CheckEnumerableElementsAnyOrder(
  1050. dict2.KeyValuePairs, new[]
  1051. {
  1052. new KeyValuePair<string, string>("Alpha", "oz"),
  1053. new KeyValuePair<string, string>("Hello", "banana"),
  1054. new KeyValuePair<string, string>("Hello", "AAA"),
  1055. new KeyValuePair<string, string>("Hi", "aaa"),
  1056. new KeyValuePair<string, string>("qubert", "hoover"),
  1057. new KeyValuePair<string, string>("qubert", "dinosaur")
  1058. });
  1059. MultiDictionary<string, int> dict4 =
  1060. new MultiDictionary<string, int>(true);
  1061. MultiDictionary<string, int> dict5;
  1062. dict5 = dict4.Clone();
  1063. Assert.False(dict4 == dict5);
  1064. Assert.True(dict4.Count == 0 && dict5.Count == 0);
  1065. dict4.Add("hello", 1);
  1066. Assert.True(dict4.Count == 1 && dict5.Count == 0);
  1067. dict5.Add("hi", 7);
  1068. dict4.Clear();
  1069. Assert.True(dict4.Count == 0 && dict5.Count == 1);
  1070. }
  1071. #endregion
  1072. #region TestAddElements (LongRunning)
  1073. /// <summary>
  1074. /// Test add elements. Note: Too slow for a dynamic unit test.
  1075. /// </summary>
  1076. [Test, Category("LongRunning")]
  1077. public static void TestAddElements()
  1078. {
  1079. // Test without duplicate values.
  1080. MultiDictionary<string, double> dict1 =
  1081. new MultiDictionary<string, double>(false);
  1082. dict1.Add("foo", 3.5);
  1083. dict1.Add("foo", -1.2);
  1084. dict1.Add(null, 11.1);
  1085. dict1.Add("foo", 8.8);
  1086. dict1.Add(null, 11.1);
  1087. dict1.Add("bar", 9.8);
  1088. dict1.Add("foo", 8.8);
  1089. dict1.Add("gib", 7.1);
  1090. dict1.Add("S", -9);
  1091. dict1.Add(null, 5.5);
  1092. Assert.Equal("3.5, -1.2, 8.8",
  1093. dict1["foo"].Write());
  1094. Assert.Equal("7.1",
  1095. dict1["gib"].Write());
  1096. Assert.Equal("11.1, 5.5",
  1097. dict1[null].Write());
  1098. // Test with duplicate values.
  1099. dict1 = new MultiDictionary<string, double>(true);
  1100. dict1.Add("foo", 3.5);
  1101. dict1.Add("foo", -1.2);
  1102. dict1.Add(null, 11.1);
  1103. dict1.Add("foo", 8.8);
  1104. dict1.Add(null, 11.1);
  1105. dict1.Add("bar", 9.8);
  1106. dict1.Add("foo", 8.8);
  1107. dict1.Add("gib", 7.1);
  1108. dict1.Add("S", -9);
  1109. dict1.Add(null, 5.5);
  1110. Assert.Equal("3.5, -1.2, 8.8, 8.8",
  1111. dict1["foo"].Write());
  1112. Assert.Equal("7.1",
  1113. dict1["gib"].Write());
  1114. Assert.Equal("11.1, 11.1, 5.5",
  1115. dict1[null].Write());
  1116. }
  1117. #endregion
  1118. #region AddManyOne (LongRunning)
  1119. /// <summary>
  1120. /// Add many One. Note: Too slow for a dynamic unit test.
  1121. /// </summary>
  1122. [Test, Category("LongRunning")]
  1123. public static void AddManyOne()
  1124. {
  1125. // Test without duplicate values.
  1126. MultiDictionary<string, double> dict1 =
  1127. new MultiDictionary<string, double>(
  1128. false, StringComparer.InvariantCultureIgnoreCase);
  1129. dict1.AddMany("foo", new[]
  1130. {
  1131. 9.8, 1.2, -9, 9.8, -9, 4
  1132. });
  1133. dict1.AddMany("hi", new double[0]);
  1134. dict1.AddMany("FOO", new double[]
  1135. {
  1136. 8, -9
  1137. });
  1138. Assert.Equal(1, dict1.Count);
  1139. Assert.True(dict1.ContainsKey("foo"));
  1140. Assert.False(dict1.ContainsKey("hi"));
  1141. CollectionBaseTests.CheckEnumerableElementsAnyOrder(
  1142. dict1.Keys, new[]
  1143. {
  1144. "foo"
  1145. });
  1146. CollectionBaseTests.CheckEnumerableElementsAnyOrder(
  1147. dict1["fOo"], new[]
  1148. {
  1149. -9, 1.2, 4, 8, 9.8
  1150. });
  1151. CollectionBaseTests.CheckEnumerableElementsAnyOrder(
  1152. dict1.KeyValuePairs, new[]
  1153. {
  1154. new KeyValuePair<string, double>("foo", -9),
  1155. new KeyValuePair<string, double>("foo", 1.2),
  1156. new KeyValuePair<string, double>("foo", 4),
  1157. new KeyValuePair<string, double>("foo", 8),
  1158. new KeyValuePair<string, double>("foo", 9.8)
  1159. });
  1160. // Test with duplicate values
  1161. dict1 = new MultiDictionary<string, double>(
  1162. true, StringComparer.InvariantCultureIgnoreCase);
  1163. dict1.AddMany("foo", new[]
  1164. {
  1165. 9.8, 1.2, -9, 9.8, -9, 4
  1166. });
  1167. dict1.AddMany("hi", new double[0]);
  1168. dict1.AddMany("a", new double[]
  1169. {
  1170. 2, 1, 2
  1171. });
  1172. dict1.AddMany("FOO", new double[]
  1173. {
  1174. 8, -9
  1175. });
  1176. Assert.Equal(2, dict1.Count);
  1177. Assert.True(dict1.ContainsKey("foo"));
  1178. Assert.False(dict1.ContainsKey("hi"));
  1179. CollectionBaseTests.CheckEnumerableElementsAnyOrder(
  1180. dict1.Keys, new[]
  1181. {
  1182. "a", "foo"
  1183. });
  1184. CollectionBaseTests.CheckEnumerableElementsAnyOrder(
  1185. dict1["fOo"], new[]
  1186. {
  1187. -9, -9, -9, 1.2, 4, 8, 9.8, 9.8
  1188. });
  1189. CollectionBaseTests.CheckEnumerableElementsAnyOrder(
  1190. dict1.KeyValuePairs, new[]
  1191. {
  1192. new KeyValuePair<string, double>("a", 1),
  1193. new KeyValuePair<string, double>("a", 2),
  1194. new KeyValuePair<string, double>("a", 2),
  1195. new KeyValuePair<string, double>("foo", -9),
  1196. new KeyValuePair<string, double>("foo", -9),
  1197. new KeyValuePair<string, double>("foo", -9),
  1198. new KeyValuePair<string, double>("foo", 1.2),
  1199. new KeyValuePair<string, double>("foo", 4),
  1200. new KeyValuePair<string, double>("foo", 8),
  1201. new KeyValuePair<string, double>("foo", 9.8),
  1202. new KeyValuePair<string, double>("foo", 9.8)
  1203. });
  1204. }
  1205. #endregion
  1206. #region Replace (LongRunning)
  1207. /// <summary>
  1208. /// Replace. Note: Too slow for a dynamic unit test.
  1209. /// </summary>
  1210. [Test, Category("LongRunning")]
  1211. public static void Replace()
  1212. {
  1213. MultiDictionary<string, int> dict1 =
  1214. new MultiDictionary<string, int>(true);
  1215. dict1.Add("foo", 4);
  1216. dict1.Add("bar", 7);
  1217. dict1.Add("foo", 6);
  1218. dict1.Add("z", 3);
  1219. dict1.Add("bar", 8);
  1220. dict1.Add("z", 3);
  1221. dict1.Add("foo", 1);
  1222. dict1.Replace("foo", 13);
  1223. dict1.Replace("z", 19);
  1224. dict1.Replace("hello", 193);
  1225. dict1.Replace("foo", 123);
  1226. dict1.Add("foo", 123);
  1227. CheckMultiDictionaryContents(dict1,
  1228. new[]
  1229. {
  1230. "bar", "foo", "hello", "z"
  1231. },
  1232. new[]
  1233. {
  1234. new[]
  1235. {
  1236. 7, 8
  1237. },
  1238. new[]
  1239. {
  1240. 123, 123
  1241. },
  1242. new[]
  1243. {
  1244. 193
  1245. },
  1246. new[]
  1247. {
  1248. 19
  1249. }
  1250. },
  1251. "sailor", 19921);
  1252. }
  1253. #endregion
  1254. #region ReplaceMany (LongRunning)
  1255. /// <summary>
  1256. /// Replace many. Note: Too slow for a dynamic unit test.
  1257. /// </summary>
  1258. [Test, Category("LongRunning")]
  1259. public static void ReplaceMany()
  1260. {
  1261. MultiDictionary<string, int> dict1 =
  1262. new MultiDictionary<string, int>(false);
  1263. dict1.Add("foo", 4);
  1264. dict1.Add("bar", 7);
  1265. dict1.Add("foo", 6);
  1266. dict1.Add("z", 3);
  1267. dict1.Add("bar", 8);
  1268. dict1.Add("z", 3);
  1269. dict1.Add("foo", 1);
  1270. dict1.Add("bill", 9);
  1271. dict1.ReplaceMany("bill", new int[0]);
  1272. dict1.ReplaceMany("foo", new[]
  1273. {
  1274. 13, 4
  1275. });
  1276. dict1.ReplaceMany("z", new[]
  1277. {
  1278. 19
  1279. });
  1280. dict1.ReplaceMany("hello", new[]
  1281. {
  1282. 193, -11, 193
  1283. });
  1284. dict1.ReplaceMany("goodbye", new int[0]);
  1285. dict1.ReplaceMany("foo", new[]
  1286. {
  1287. 123, 0, 4
  1288. });
  1289. dict1.Add("foo", 29);
  1290. CheckMultiDictionaryContents(dict1,
  1291. new[]
  1292. {
  1293. "bar", "foo", "hello", "z"
  1294. },
  1295. new[]
  1296. {
  1297. new[]
  1298. {
  1299. 7, 8
  1300. },
  1301. new[]
  1302. {
  1303. 0, 4, 29, 123
  1304. },
  1305. new[]
  1306. {
  1307. -11, 193
  1308. },
  1309. new[]
  1310. {
  1311. 19
  1312. }
  1313. },
  1314. "sailor", 19921);
  1315. }
  1316. #endregion
  1317. #region RemoveKey (LongRunning)
  1318. /// <summary>
  1319. /// Remove key. Note: Too slow for a dynamic unit test.
  1320. /// </summary>
  1321. [Test, Category("LongRunning")]
  1322. public static void RemoveKey()
  1323. {
  1324. MultiDictionary<string, int> dict1 =
  1325. new MultiDictionary<string, int>(true);
  1326. dict1.Add("foo", 4);
  1327. dict1.Add("bar", 7);
  1328. dict1.Add("foo", 6);
  1329. dict1.Add("z", 3);
  1330. dict1.Add("bar", 8);
  1331. dict1.Add("z", 10);
  1332. dict1.Add("z", 3);
  1333. dict1.Add("foo", 4);
  1334. dict1.Add("bill", 9);
  1335. Assert.True(dict1.ContainsKey("bill"));
  1336. Assert.True(dict1.ContainsKey("foo"));
  1337. Assert.True(dict1.ContainsKey("z"));
  1338. Assert.True(dict1.Remove("bill"));
  1339. Assert.False(dict1.Remove("bill"));
  1340. Assert.False(dict1.Remove("smell"));
  1341. Assert.True(dict1.Remove("foo"));
  1342. CheckMultiDictionaryContents(dict1,
  1343. new[]
  1344. {
  1345. "bar", "z"
  1346. },
  1347. new[]
  1348. {
  1349. new[]
  1350. {
  1351. 7, 8
  1352. }, new[]
  1353. {
  1354. 3, 3, 10
  1355. }
  1356. },
  1357. "sailor", 19921);
  1358. }
  1359. #endregion
  1360. #region RemoveManyKeys (LongRunning)
  1361. /// <summary>
  1362. /// Remove many keys. Note: Too slow for a dynamic unit test.
  1363. /// </summary>
  1364. [Test, Category("LongRunning")]
  1365. public static void RemoveManyKeys()
  1366. {
  1367. MultiDictionary<string, int> dict1 =
  1368. new MultiDictionary<string, int>(true);
  1369. dict1.Add("foo", 4);
  1370. dict1.Add("bar", 7);
  1371. dict1.Add("foo", 6);
  1372. dict1.Add("z", 3);
  1373. dict1.Add("bar", 8);
  1374. dict1.Add("z", 10);
  1375. dict1.Add("z", 3);
  1376. dict1.Add("foo", 4);
  1377. dict1.Add("bill", 9);
  1378. Assert.True(dict1.ContainsKey("bill"));
  1379. Assert.True(dict1.ContainsKey("foo"));
  1380. Assert.True(dict1.ContainsKey("z"));
  1381. Assert.Equal(2, dict1.RemoveMany(
  1382. new[]
  1383. {
  1384. "bill", "smell", "foo", "bill"
  1385. }));
  1386. CheckMultiDictionaryContents(dict1,
  1387. new[]
  1388. {
  1389. "bar", "z"
  1390. },
  1391. new[]
  1392. {
  1393. new[]
  1394. {
  1395. 7, 8
  1396. }, new[]
  1397. {
  1398. 3, 3, 10
  1399. }
  1400. },
  1401. "sailor", 19921);
  1402. }
  1403. #endregion
  1404. #region Remove (LongRunning)
  1405. /// <summary>
  1406. /// Remove. Note: Too slow for a dynamic unit test.
  1407. /// </summary>
  1408. [Test, Category("LongRunning")]
  1409. public static void Remove()
  1410. {
  1411. MultiDictionary<string, int> dict1 =
  1412. new MultiDictionary<string, int>(true);
  1413. dict1.Add("foo", 4);
  1414. dict1.Add("bar", 7);
  1415. dict1.Add("foo", 6);
  1416. dict1.Add("z", 3);
  1417. dict1.Add("bar", 8);
  1418. dict1.Add("z", 10);
  1419. dict1.Add("z", 3);
  1420. dict1.Add("foo", 4);
  1421. dict1.Add("bill", 9);
  1422. dict1.Add("foo", 4);
  1423. Assert.True(dict1.Remove("foo", 4));
  1424. Assert.True(dict1.Remove("foo", 4));
  1425. Assert.True(dict1.Remove("z", 10));
  1426. Assert.False(dict1.Remove("z", 10));
  1427. Assert.False(dict1.Remove("foo", 11));
  1428. Assert.False(dict1.Remove(null, 0));
  1429. Assert.True(dict1.Remove("bill", 9));
  1430. CheckMultiDictionaryContents(dict1,
  1431. new[]
  1432. {
  1433. "bar", "foo", "z"
  1434. },
  1435. new[]
  1436. {
  1437. new[]
  1438. {
  1439. 7, 8
  1440. },
  1441. new[]
  1442. {
  1443. 4, 6
  1444. },
  1445. new[]
  1446. {
  1447. 3, 3
  1448. }
  1449. },
  1450. "sailor", 19921);
  1451. }
  1452. #endregion
  1453. #region RemoveManyOne (LongRunning)
  1454. /// <summary>
  1455. /// Remove many One. Note: Too slow for a dynamic unit test.
  1456. /// </summary>
  1457. [Test, Category("LongRunning")]
  1458. public static void RemoveManyOne()
  1459. {
  1460. MultiDictionary<string, int> dict1 =
  1461. new MultiDictionary<string, int>(true);
  1462. dict1.Add("bill", 7);
  1463. dict1.Add("foo", 4);
  1464. dict1.Add("bar", 7);
  1465. dict1.Add("foo", 6);
  1466. dict1.Add("z", 3);
  1467. dict1.Add("bar", 8);
  1468. dict1.Add("z", 10);
  1469. dict1.Add("z", 3);
  1470. dict1.Add("foo", 4);
  1471. dict1.Add("bill", 9);
  1472. dict1.Add("foo", 4);
  1473. Assert.Equal(2, dict1.RemoveMany("foo", new[]
  1474. {
  1475. 4, 11, 4
  1476. }));
  1477. Assert.Equal(1, dict1.RemoveMany("z", new[]
  1478. {
  1479. 9, 2, 10
  1480. }));
  1481. Assert.Equal(0, dict1.RemoveMany("z", new[]
  1482. {
  1483. 10, 16, 144, 10
  1484. }));
  1485. Assert.Equal(0, dict1.RemoveMany("foo", new int[0]));
  1486. Assert.Equal(0, dict1.RemoveMany(null, new int[2]
  1487. {
  1488. 1, 2
  1489. }));
  1490. Assert.Equal(2, dict1.RemoveMany("bill", new[]
  1491. {
  1492. 9, 7
  1493. }));
  1494. CheckMultiDictionaryContents(dict1,
  1495. new[]
  1496. {
  1497. "bar", "foo", "z"
  1498. },
  1499. new[]
  1500. {
  1501. new[]
  1502. {
  1503. 7, 8
  1504. },
  1505. new[]
  1506. {
  1507. 4, 6
  1508. },
  1509. new[]
  1510. {
  1511. 3, 3
  1512. }
  1513. },
  1514. "sailor", 19921);
  1515. }
  1516. #endregion
  1517. #region Clear (LongRunning)
  1518. /// <summary>
  1519. /// Clear. Note: Too slow for a dynamic unit test.
  1520. /// </summary>
  1521. [Test, Category("LongRunning")]
  1522. public static void Clear()
  1523. {
  1524. MultiDictionary<string, int> dict1 =
  1525. new MultiDictionary<string, int>(true);
  1526. dict1.Add("foo", 4);
  1527. dict1.Add("bill", 7);
  1528. dict1.Add("foo", 4);
  1529. dict1.Add("bar", 7);
  1530. dict1.Add("foo", 6);
  1531. dict1.Add("z", 3);
  1532. dict1.Add("bar", 8);
  1533. dict1.Add("z", 10);
  1534. dict1.Add(null, 3);
  1535. dict1.Add("foo", 4);
  1536. dict1.Add("bill", 9);
  1537. dict1.Add("foo", 4);
  1538. dict1.Clear();
  1539. Assert.Equal(0, dict1.Count);
  1540. Assert.False(dict1.ContainsKey("foo"));
  1541. Assert.False(dict1.ContainsKey("z"));
  1542. Assert.False(dict1.ContainsKey(null));
  1543. Assert.Equal(0, dict1.Keys.Count);
  1544. Assert.Equal(0, dict1.Values.Count);
  1545. Assert.Equal(0, dict1.KeyValuePairs.Count);
  1546. CheckMultiDictionaryContents(dict1, new string[0],
  1547. new int[0][], "foo", 4);
  1548. }
  1549. #endregion
  1550. #region Count (LongRunning)
  1551. /// <summary>
  1552. /// Count. Note: Too slow for a dynamic unit test.
  1553. /// </summary>
  1554. [Test, Category("LongRunning")]
  1555. public static void Count()
  1556. {
  1557. MultiDictionary<string, int> dict1 =
  1558. new MultiDictionary<string, int>(true);
  1559. dict1.Add("foo", 4);
  1560. dict1.Add(null, 7);
  1561. dict1.Add("bar", 11);
  1562. dict1.Add("foo", 7);
  1563. dict1.Add(null, 7);
  1564. dict1.Add("hello", 11);
  1565. dict1.Add("foo", 4);
  1566. Assert.Equal(4, dict1.Count);
  1567. MultiDictionary<string, int> dict2 =
  1568. new MultiDictionary<string, int>(false);
  1569. dict2.Add("foo", 4);
  1570. dict2.Add(null, 7);
  1571. dict2.Add("bar", 11);
  1572. dict2.Add("foo", 7);
  1573. dict2.Add(null, 7);
  1574. dict2.Add("hello", 11);
  1575. dict2.Add("foo", 4);
  1576. Assert.Equal(4, dict2.Count);
  1577. dict2.Remove("foo");
  1578. Assert.Equal(3, dict2.Count);
  1579. dict2.Clear();
  1580. Assert.Equal(0, dict2.Count);
  1581. }
  1582. #endregion
  1583. #region ContainsKey (LongRunning)
  1584. /// <summary>
  1585. /// Contains key. Note: Too slow for a dynamic unit test.
  1586. /// </summary>
  1587. [Test, Category("LongRunning")]
  1588. public static void ContainsKey()
  1589. {
  1590. MultiDictionary<string, int> dict1 =
  1591. new MultiDictionary<string, int>(true);
  1592. dict1.Add("foo", 4);
  1593. dict1.Add(null, 7);
  1594. dict1.Add("bar", 11);
  1595. dict1.Add("foo", 7);
  1596. dict1.Add(null, 7);
  1597. dict1.Add("hello", 11);
  1598. dict1.Add("foo", 4);
  1599. Assert.True(dict1.ContainsKey(null));
  1600. Assert.True(dict1.ContainsKey("foo"));
  1601. Assert.True(dict1

Large files files are truncated, but you can click here to view the full file