**** Merged from MCS ****
[mono-project.git] / mcs / class / Mono.C5 / linkedlists / HashedLinkedLIst.cs
blobdab32392680ec0b81f10024dcb09e567e011ad33
1 #if NET_2_0
2 /*
3 Copyright (c) 2003-2004 Niels Kokholm <kokholm@itu.dk> and Peter Sestoft <sestoft@dina.kvl.dk>
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
23 #define LISTORDERnot
24 #define EXTLISTORDER
25 using System;
26 using System.Diagnostics;
27 using MSG=System.Collections.Generic;
29 namespace C5
31 /// <summary>
32 /// A list collection based on a doubly linked list data structure with
33 /// a hash index mapping item to node.
34 /// </summary>
35 public class HashedLinkedList<T>: LinkedList<T>, IList<T>
37 #region Fields
39 HashDictionary<T,Node> dict;
41 //Invariant: base.underlying == basehashedlist
42 HashedLinkedList<T> hashedunderlying;
44 #endregion
46 #region Constructors
48 void init()
50 #if LISTORDER || EXTLISTORDER
51 maintaintags = true;
52 #endif
53 dict = new HashDictionary<T,Node>(itemhasher);
57 /// <summary>
58 /// Create a hashed linked list with an external item hasher.
59 /// </summary>
60 /// <param name="itemhasher">The external hasher</param>
61 public HashedLinkedList(IHasher<T> itemhasher) : base(itemhasher) { init(); }
64 /// <summary>
65 /// Create a hashed linked list with the natural item hasher.
66 /// </summary>
67 public HashedLinkedList() : base() { init(); }
69 #endregion
71 #region Util
73 bool contains(T item, out Node node)
75 if (!dict.Find(item,out node))
76 return false;
77 else
78 return insideview(node);
82 void insert(Node succ, T item)
84 Node newnode = new Node(item);
86 if (dict.FindOrAdd(item, ref newnode))
87 throw new ArgumentException("Item already in indexed list");
89 insertNode(succ, newnode);
93 private bool dictremove(T item, out Node node)
95 if (hashedunderlying == null)
97 if (!dict.Remove(item, out node))
98 return false;
100 else
102 //We cannot avoid calling dict twice - have to intersperse the listorder test!
103 if (!contains(item, out node))
104 return false;
106 dict.Remove(item);
109 return true;
113 bool insideview(Node node)
115 if (underlying == null)
116 return true;
118 #if LISTORDER
119 if (maintaintags)
120 return (startsentinel.tag < node.tag && (endsentinel.tag == 0 || node.tag < endsentinel.tag));
121 else
122 #elif EXTLISTORDER
123 if (maintaintags)
124 return (startsentinel.precedes(node) && node.precedes(endsentinel));
125 else
126 #endif
127 if (2 * size < hashedunderlying.size)
129 Node cursor = startsentinel.next;
131 while (cursor != endsentinel)
133 if (cursor == node)
134 return true;
136 cursor = cursor.next;
139 return false;
141 else
143 Node cursor = hashedunderlying.startsentinel.next;
145 while (cursor != startsentinel.next)
147 if (cursor == node)
148 return false;
150 cursor = cursor.next;
153 cursor = endsentinel;
154 while (cursor != hashedunderlying.endsentinel)
156 if (cursor == node)
157 return false;
159 cursor = cursor.next;
162 return true;
167 #endregion
169 #region IList<T> Members
171 /// <summary>
172 /// On this list, this indexer is read/write.
173 /// <exception cref="IndexOutOfRangeException"/> if i is negative or
174 /// &gt;= the size of the collection.
175 /// </summary>
176 /// <value>The i'th item of this list.</value>
177 /// <param name="i">The index of the item to fetch or store.</param>
178 [Tested]
179 public override T this[int i]
181 [Tested]
184 modifycheck();
185 return base[i];
187 [Tested]
190 updatecheck();
192 Node n = get(i);
194 if (itemhasher.Equals(value, n.item))
196 n.item = value;
197 dict.Update(value, n);
199 else if (!dict.FindOrAdd(value, ref n))
201 dict.Remove(n.item);
202 n.item = value;
204 else
205 throw new ArgumentException("Item already in indexed list");
210 /// <summary>
211 /// Insert an item at a specific index location in this list.
212 /// <exception cref="IndexOutOfRangeException"/> if i is negative or
213 /// &gt; the size of the collection.</summary>
214 /// <exception cref="InvalidOperationException"/> if the item is
215 /// already in the list.
216 /// <param name="i">The index at which to insert.</param>
217 /// <param name="item">The item to insert.</param>
218 [Tested]
219 public override void Insert(int i, T item)
221 updatecheck();
222 insert(i == size ? endsentinel : get(i), item);
226 /// <summary>
227 /// Insert into this list all items from an enumerable collection starting
228 /// at a particular index.
229 /// <exception cref="IndexOutOfRangeException"/> if i is negative or
230 /// &gt; the size of the collection.
231 /// <exception cref="InvalidOperationException"/> if one of the items to insert is
232 /// already in the list.
233 /// </summary>
234 /// <param name="i">Index to start inserting at</param>
235 /// <param name="items">Items to insert</param>
236 [Tested]
237 public override void InsertAll(int i, MSG.IEnumerable<T> items)
239 updatecheck();
241 Node succ, node;
242 int count = 0;
244 succ = i == size ? endsentinel : get(i);
245 node = succ.prev;
246 #if LISTORDER
247 //TODO: guard?
248 int taglimit = i == size ? int.MaxValue : succ.tag - 1, thetag = node.tag;
249 #elif EXTLISTORDER
250 TagGroup taggroup = null;
251 int taglimit = 0, thetag = 0;
253 if (maintaintags)
254 taggroup = gettaggroup(node, succ, out thetag, out taglimit);
255 #endif
256 foreach (T item in items)
258 Node tmp = new Node(item, node, null);
260 if (!dict.FindOrAdd(item, ref tmp))
262 #if LISTORDER
263 if (maintaintags)
264 tmp.tag = thetag < taglimit ? ++thetag : thetag;
265 #elif EXTLISTORDER
266 if (maintaintags)
268 tmp.tag = thetag < taglimit ? ++thetag : thetag;
269 tmp.taggroup = taggroup;
271 #endif
272 node.next = tmp;
273 count++;
274 node = tmp;
276 else
277 throw new ArgumentException("Item already in indexed list");
280 #if EXTLISTORDER
281 if (maintaintags)
283 taggroup.count += count;
284 taggroup.first = succ.prev;
285 taggroup.last = node;
287 #endif
288 succ.prev = node;
289 node.next = succ;
290 size += count;
291 if (hashedunderlying != null)
292 hashedunderlying.size += count;
293 #if LISTORDER
294 if (maintaintags && node.tag == node.prev.tag)
295 settag(node);
296 #elif EXTLISTORDER
297 if (maintaintags)
299 if (node.tag == node.prev.tag)
300 splittaggroup(taggroup);
302 #endif
306 /// <summary>
307 /// Insert an item right before the first occurrence of some target item.
308 /// <exception cref="ArgumentException"/> if target is not found
309 /// <exception cref="InvalidOperationException"/> if the item is
310 /// already in the list.
311 /// </summary>
312 /// <param name="item">The item to insert.</param>
313 /// <param name="target">The target before which to insert.</param>
314 [Tested]
315 public override void InsertBefore(T item, T target)
317 updatecheck();
319 Node node;
321 if (!contains(target, out node))
322 throw new ArgumentException("Target item not found");
324 insert(node, item);
328 /// <summary>
329 /// Insert an item right after the last(???) occurrence of some target item.
330 /// <exception cref="ArgumentException"/> if target is not found
331 /// <exception cref="InvalidOperationException"/> if the item is
332 /// already in the list.
333 /// </summary>
334 /// <param name="item">The item to insert.</param>
335 /// <param name="target">The target after which to insert.</param>
336 [Tested]
337 public override void InsertAfter(T item, T target)
339 updatecheck();
341 Node node;
343 if (!contains(target, out node))
344 throw new ArgumentException("Target item not found");
346 insert(node.next, item);
351 /// <summary>
352 /// Insert an item at the front of this list.
353 /// <exception cref="InvalidOperationException"/> if the item is
354 /// already in the list.
355 /// </summary>
356 /// <param name="item">The item to insert.</param>
357 [Tested]
358 public override void InsertFirst(T item)
360 updatecheck();
361 insert(startsentinel.next, item);
364 /// <summary>
365 /// Insert an item at the back of this list.
366 /// <exception cref="InvalidOperationException"/> if the item is
367 /// already in the list.
368 /// </summary>
369 /// <param name="item">The item to insert.</param>
370 [Tested]
371 public override void InsertLast(T item)
373 updatecheck();
374 insert(endsentinel, item);
378 /// <summary>
379 /// Remove one item from the list: from the front if <code>FIFO</code>
380 /// is true, else from the back.
381 /// <exception cref="InvalidOperationException"/> if this list is empty.
382 /// </summary>
383 /// <returns>The removed item.</returns>
384 [Tested]
385 public override T Remove()
387 T retval = base.Remove();
389 dict.Remove(retval);
390 return retval;
394 /// <summary>
395 /// Remove one item from the front of the list.
396 /// <exception cref="InvalidOperationException"/> if this list is empty.
397 /// </summary>
398 /// <returns>The removed item.</returns>
399 [Tested]
400 public override T RemoveFirst()
402 T retval = base.RemoveFirst();
404 dict.Remove(retval);
405 return retval;
409 /// <summary>
410 /// Remove one item from the back of the list.
411 /// <exception cref="InvalidOperationException"/> if this list is empty.
412 /// </summary>
413 /// <returns>The removed item.</returns>
414 [Tested]
415 public override T RemoveLast()
417 T retval = base.RemoveLast();
419 dict.Remove(retval);
420 return retval;
424 /// <summary>
425 /// Create a list view on this list.
426 /// <exception cref="ArgumentOutOfRangeException"/> if the view would not fit into
427 /// this list.
428 /// </summary>
429 /// <param name="start">The index in this list of the start of the view.</param>
430 /// <param name="count">The size of the view.</param>
431 /// <returns>The new list view.</returns>
432 [Tested]
433 public override IList<T> View(int start, int count)
435 checkRange(start, count);
436 modifycheck();
438 HashedLinkedList<T> retval = (HashedLinkedList<T>)MemberwiseClone();
440 retval.underlying = retval.hashedunderlying = hashedunderlying != null ? hashedunderlying : this;
441 retval.offset = start + offset;
442 retval.startsentinel = start == 0 ? startsentinel : get(start - 1);
443 retval.endsentinel = start + count == size ? endsentinel : get(start + count);
444 retval.size = count;
445 return retval;
448 /// <summary>
449 /// Reverst part of the list so the items are in the opposite sequence order.
450 /// <exception cref="ArgumentException"/> if the count is negative.
451 /// <exception cref="ArgumentOutOfRangeException"/> if the part does not fit
452 /// into the list.
453 /// </summary>
454 /// <param name="start">The index of the start of the part to reverse.</param>
455 /// <param name="count">The size of the part to reverse.</param>
456 [Tested]
457 public override void Reverse(int start, int count)
459 //Duplicating linkedlist<T> code to minimize cache misses
460 updatecheck();
461 checkRange(start, count);
462 if (count == 0)
463 return;
465 Node a = get(start), b = get(start + count - 1);
467 for (int i = 0; i < count / 2; i++)
469 T swap = a.item;a.item = b.item;b.item = swap;
470 dict[a.item] = a;dict[b.item] = b;
471 a = a.next;b = b.prev;
476 /// <summary>
477 /// Shuffle the items of this list according to a specific random source.
478 /// </summary>
479 /// <param name="rnd">The random source.</param>
480 public override void Shuffle(Random rnd)
482 updatecheck();
484 ArrayList<T> a = new ArrayList<T>();
486 a.AddAll(this);
487 a.Shuffle(rnd);
489 Node cursor = startsentinel.next;
490 int j = 0;
492 while (cursor != endsentinel)
494 dict[cursor.item = a[j++]] = cursor;
495 cursor = cursor.next;
499 #endregion
501 #region IIndexed<T> Members
503 /// <summary>
504 /// Searches for an item in the list going forwrds from the start.
505 /// </summary>
506 /// <param name="item">Item to search for.</param>
507 /// <returns>Index of item from start.</returns>
508 [Tested]
509 public override int IndexOf(T item)
511 Node node;
513 modifycheck();
514 if (!dict.Find(item, out node))
515 return -1;
516 #if LISTORDER
517 if (maintaintags && !insideview(node))
518 return -1;
519 #elif EXTLISTORDER
520 if (maintaintags && !insideview(node))
521 return -1;
522 #endif
523 return base.IndexOf(item);
527 /// <summary>
528 /// Searches for an item in the list going backwords from the end.
529 /// </summary>
530 /// <param name="item">Item to search for.</param>
531 /// <returns>Index of of item from the end.</returns>
532 [Tested]
533 public override int LastIndexOf(T item) { return IndexOf(item); }
536 /// <summary>
537 /// Remove the item at a specific position of the list.
538 /// <exception cref="IndexOutOfRangeException"/> if i is negative or
539 /// &gt;= the size of the collection.
540 /// </summary>
541 /// <param name="i">The index of the item to remove.</param>
542 /// <returns>The removed item.</returns>
543 [Tested]
544 public override T RemoveAt(int i)
546 T retval = base.RemoveAt(i);
548 dict.Remove(retval);
549 return retval;
553 /// <summary>
554 /// Remove all items in an index interval.
555 /// <exception cref="IndexOutOfRangeException"/>???.
556 /// </summary>
557 /// <param name="start">The index of the first item to remove.</param>
558 /// <param name="count">The number of items to remove.</param>
559 [Tested]
560 public override void RemoveInterval(int start, int count)
562 updatecheck();
563 checkRange(start, count);
564 if (count == 0)
565 return;
567 Node a, b;
569 if (start <= size - start - count)
571 b = a = get(start);
572 #if EXTLISTORDER
573 Node c = a.prev;
574 #endif
575 for (int i = 0; i < count; i++)
577 dict.Remove(b.item);
578 #if EXTLISTORDER
579 if (maintaintags)
581 c.next = b;
582 b.prev = c;
583 removefromtaggroup(b);
585 #endif
586 b = b.next;
589 a.prev.next = b;
590 b.prev = a.prev;
592 else
594 a = b = get(start + count - 1);
595 #if EXTLISTORDER
596 Node c = b.next;
597 #endif
598 for (int i = 0; i < count; i++)
600 dict.Remove(a.item);
601 #if EXTLISTORDER
602 if (maintaintags)
604 c.prev = a;
605 a.next = c;
606 removefromtaggroup(a);
608 #endif
609 a = a.prev;
612 a.next = b.next;
613 b.next.prev = a;
616 size -= count;
617 if (hashedunderlying != null)
618 hashedunderlying.size -= count;
621 #endregion
623 #region ISequenced<T> Members
625 int ISequenced<T>.GetHashCode()
626 { modifycheck(); return sequencedhashcode(); }
629 bool ISequenced<T>.Equals(ISequenced<T> that)
630 { modifycheck(); return sequencedequals(that); }
632 #endregion
634 #region IEditableCollection<T> Members
637 /// <summary>
638 /// The value is symbolic indicating the type of asymptotic complexity
639 /// in terms of the size of this collection (worst-case or amortized as
640 /// relevant).
641 /// </summary>
642 /// <value>Speed.Constant</value>
643 [Tested]
644 public override Speed ContainsSpeed
646 [Tested]
649 #if LISTORDER || EXTLISTORDER
650 return hashedunderlying == null || maintaintags ? Speed.Constant : Speed.Linear;
651 #else
652 return basehashedlist == null ? Speed.Constant : Speed.Linear;
653 #endif
658 int ICollection<T>.GetHashCode()
659 { modifycheck(); return unsequencedhashcode(); }
662 bool ICollection<T>.Equals(ICollection<T> that)
663 { modifycheck(); return unsequencedequals(that); }
666 /// <summary>
667 /// Check if this collection contains (an item equivalent to according to the
668 /// itemhasher) a particular value.
669 /// </summary>
670 /// <param name="item">The value to check for.</param>
671 /// <returns>True if the items is in this collection.</returns>
672 [Tested]
673 public override bool Contains(T item)
675 Node node;
677 modifycheck();
678 return contains(item, out node);
682 /// <summary>
683 /// Check if this collection contains an item equivalent according to the
684 /// itemhasher to a particular value. If so, return in the ref argument (a
685 /// binary copy of) the actual value found.
686 /// </summary>
687 /// <param name="item">The value to look for.</param>
688 /// <returns>True if the items is in this collection.</returns>
689 [Tested]
690 public override bool Find(ref T item)
692 Node node;
694 modifycheck();
695 if (contains(item, out node)) { item = node.item; return true; }
697 return false;
701 /// <summary>
702 /// Check if this collection contains an item equivalent according to the
703 /// itemhasher to a particular value. If so, update the item in the collection
704 /// to with a binary copy of the supplied value.
705 /// </summary>
706 /// <param name="item">Value to update.</param>
707 /// <returns>True if the item was found and hence updated.</returns>
708 [Tested]
709 public override bool Update(T item)
711 Node node;
713 updatecheck();
714 if (contains(item, out node)) { node.item = item; return true; }
716 return false;
720 /// <summary>
721 /// Check if this collection contains an item equivalent according to the
722 /// itemhasher to a particular value. If so, return in the ref argument (a
723 /// binary copy of) the actual value found. Else, add the item to the collection.
724 /// </summary>
725 /// <param name="item">The value to look for.</param>
726 /// <returns>True if the item was found (hence not added).</returns>
727 [Tested]
728 public override bool FindOrAdd(ref T item)
730 updatecheck();
732 //This is an extended myinsert:
733 Node node = new Node(item);
735 if (!dict.FindOrAdd(item, ref node))
737 insertNode(endsentinel, node);
738 return false;
741 if (!insideview(node))
742 throw new ArgumentException("Item alredy in indexed list but outside view");
744 item = node.item;
745 return true;
749 /// <summary>
750 /// Check if this collection contains an item equivalent according to the
751 /// itemhasher to a particular value. If so, update the item in the collection
752 /// to with a binary copy of the supplied value; else add the value to the collection.
753 /// </summary>
754 /// <param name="item">Value to add or update.</param>
755 /// <returns>True if the item was found and updated (hence not added).</returns>
756 [Tested]
757 public override bool UpdateOrAdd(T item)
759 updatecheck();
761 Node node = new Node(item);
763 /*if (basehashedlist == null)
765 if (!dict.UpdateOrAdd(item, node))
766 return false;
768 else
770 //NOTE: it is hard to do this without double access to the dictionary
771 //in the update case
772 if (dict.FindOrAdd(item, ref node))
774 if (!insideview(node))
775 throw new ArgumentException("Item in indexed list but outside view");
777 //dict.Update(item, node);
778 node.item = item;
779 return true;
783 insertNode(endsentinel, node);
784 return false;
788 /// <summary>
789 /// Remove a particular item from this collection.
790 /// </summary>
791 /// <param name="item">The value to remove.</param>
792 /// <returns>True if the item was found (and removed).</returns>
793 [Tested]
794 public override bool Remove(T item)
796 updatecheck();
798 Node node;
800 if (!dictremove(item, out node))
801 return false;
803 remove(node);
804 return true;
808 /// <summary>
809 /// Remove a particular item from this collection if found.
810 /// If an item was removed, report a binary copy of the actual item removed in
811 /// the argument.
812 /// </summary>
813 /// <param name="item">The value to remove on input.</param>
814 /// <returns>True if the item was found (and removed).</returns>
815 [Tested]
816 public override bool RemoveWithReturn(ref T item)
818 Node node;
820 updatecheck();
821 if (!dictremove(item, out node))
822 return false;
824 item = node.item;
825 remove(node);
826 return true;
830 /// <summary>
831 /// Remove all items in another collection from this one.
832 /// </summary>
833 /// <param name="items">The items to remove.</param>
834 [Tested]
835 public override void RemoveAll(MSG.IEnumerable<T> items)
837 Node node;
839 updatecheck();
840 foreach (T item in items)
841 if (dictremove(item, out node))
842 remove(node);
846 /// <summary>
847 /// Remove all items from this collection.
848 /// </summary>
849 [Tested]
850 public override void Clear()
852 updatecheck();
853 if (hashedunderlying == null)
854 dict.Clear();
855 else
856 foreach (T item in this)
857 dict.Remove(item);
859 base.Clear();
863 /// <summary>
864 /// Remove all items not in some other collection from this one.
865 /// </summary>
866 /// <param name="items">The items to retain.</param>
867 [Tested]
868 public override void RetainAll(MSG.IEnumerable<T> items)
870 updatecheck();
871 if (hashedunderlying == null)
873 HashDictionary<T,Node> newdict = new HashDictionary<T,Node>(itemhasher);
875 foreach (T item in items)
877 Node node;
879 if (dict.Remove(item, out node))
880 newdict.Add(item, node);
883 foreach (KeyValuePair<T,Node> pair in dict)
885 Node n = pair.value, p = n.prev, s = n.next; s.prev = p; p.next = s;
886 #if EXTLISTORDER
887 if (maintaintags)
888 removefromtaggroup(n);
889 #endif
892 dict = newdict;
893 size = dict.Count;
894 //For a small number of items to retain it might be faster to
895 //iterate through the list and splice out the chunks not needed
897 else
899 HashSet<T> newdict = new HashSet<T>(itemhasher);
901 foreach (T item in this)
902 newdict.Add(item);
904 foreach (T item in items)
905 newdict.Remove(item);
907 Node n = startsentinel.next;
909 while (n != endsentinel)
911 if (newdict.Contains(n.item))
913 dict.Remove(n.item);
914 remove(n);
917 n = n.next;
922 /// <summary>
923 /// Check if this collection contains all the values in another collection
924 /// Multiplicities
925 /// are not taken into account.
926 /// </summary>
927 /// <param name="items">The </param>
928 /// <returns>True if all values in <code>items</code>is in this collection.</returns>
929 [Tested]
930 public override bool ContainsAll(MSG.IEnumerable<T> items)
932 Node node;
934 modifycheck();
935 foreach (T item in items)
936 if (!contains(item, out node))
937 return false;
939 return true;
943 /// <summary>
944 /// Count the number of items of the collection equal to a particular value.
945 /// Returns 0 if and only if the value is not in the collection.
946 /// </summary>
947 /// <param name="item">The value to count.</param>
948 /// <returns>The number of copies found.</returns>
949 [Tested]
950 public override int ContainsCount(T item) { return Contains(item) ? 1 : 0; }
953 /// <summary>
954 /// Remove all items equivalent to a given value.
955 /// </summary>
956 /// <param name="item">The value to remove.</param>
957 [Tested]
958 public override void RemoveAllCopies(T item) { Remove(item); }
960 #endregion
962 #region ISink<T> Members
964 /// <summary>
965 ///
966 /// </summary>
967 /// <value>False since this collection has set semantics.</value>
968 [Tested]
969 public override bool AllowsDuplicates { [Tested]get { return false; } }
972 //This is *not* the same as AddLast!!
973 /// <summary>
974 /// Add an item to this collection if possible. Since this collection has set
975 /// semantics, the item will be added if not already in the collection.
976 /// </summary>
977 /// <param name="item">The item to add.</param>
978 /// <returns>True if item was added.</returns>
979 [Tested]
980 public override bool Add(T item)
982 updatecheck();
984 Node node = new Node(item);
986 if (!dict.FindOrAdd(item, ref node))
988 insertNode(endsentinel, node);
989 return true;
992 return false;
996 //Note: this is *not* equivalent to InsertRange int this Set situation!!!
997 /// <summary>
998 /// Add the elements from another collection to this collection.
999 /// Only items not already in the collection
1000 /// will be added.
1001 /// </summary>
1002 /// <param name="items">The items to add.</param>
1003 [Tested]
1004 public override void AddAll(MSG.IEnumerable<T> items)
1006 updatecheck();
1007 foreach (T item in items)
1009 Node node = new Node(item);
1011 if (!dict.FindOrAdd(item, ref node))
1012 insertNode(endsentinel, node);
1016 /// <summary>
1017 /// Add the elements from another collection with a more specialized item type
1018 /// to this collection.
1019 /// Only items not already in the collection
1020 /// will be added.
1021 /// </summary>
1022 /// <typeparam name="U">The type of items to add</typeparam>
1023 /// <param name="items">The items to add</param>
1024 public override void AddAll<U>(MSG.IEnumerable<U> items) //where U:T
1026 updatecheck();
1027 foreach (T item in items)
1029 Node node = new Node(item);
1031 if (!dict.FindOrAdd(item, ref node))
1032 insertNode(endsentinel, node);
1036 #endregion
1038 #region IDirectedEnumerable<T> Members
1040 IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); }
1043 #endregion
1045 #region Diagnostics
1046 /// <summary>
1047 /// Check the integrity of the internal data structures of this collection.
1048 /// Only avaliable in DEBUG builds???
1049 /// </summary>
1050 /// <returns>True if check does not fail.</returns>
1051 [Tested]
1052 public override bool Check()
1054 if (!base.Check())
1055 return false;
1057 bool retval = true;
1059 if (hashedunderlying == null)
1061 if (size != dict.Count)
1063 Console.WriteLine("list.size ({0}) != dict.Count ({1})", size, dict.Count);
1064 retval = false;
1067 Node n = startsentinel.next, n2;
1069 while (n != endsentinel)
1071 if (!dict.Find(n.item, out n2))
1073 Console.WriteLine("Item in list but not dict: {0}", n.item);
1074 retval = false;
1076 else if (n != n2)
1078 Console.WriteLine("Wrong node in dict for item: {0}", n.item);
1079 retval = false;
1082 n = n.next;
1086 return retval;
1088 #endregion
1091 #endif