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
24 using System
.Diagnostics
;
25 using MSG
= System
.Collections
.Generic
;
29 /// A set collection based on a dynamic array combined with a hash index
30 /// for item to index lookup.
32 public class HashedArrayList
<T
>: ArrayList
<T
>, IList
<T
>
36 HashSet
<KeyValuePair
<T
,int>> index
;
42 private void reindex(int start
) { reindex(start, underlyingsize); }
45 private void reindex(int start
, int end
)
47 for (int j
= start
; j
< end
; j
++)
48 index
.Update(new KeyValuePair
<T
,int>(array
[j
], j
));
53 /// Internal version of IndexOf without modification checks.
55 /// <param name="item">Item to look for</param>
56 /// <returns>The index of first occurrence</returns>
57 protected override int indexOf(T item
)
59 KeyValuePair
<T
,int> p
= new KeyValuePair
<T
,int>(item
);
61 if (!index
.Find(ref p
) || p
.value < offset
|| p
.value >= offset
+ size
)
64 return p
.value - offset
;
69 /// Internal version of LastIndexOf without modification checks.
71 /// <param name="item">Item to look for</param>
72 /// <returns>The index of last occurrence</returns>
73 protected override int lastIndexOf(T item
) { return indexOf(item); }
77 /// Internal version of Insert with no modification checks.
78 /// <exception cref="ArgumentException"/> if item already in list.
80 /// <param name="i">Index to insert at</param>
81 /// <param name="item">Item to insert</param>
82 protected override void insert(int i
, T item
)
84 KeyValuePair
<T
,int> p
= new KeyValuePair
<T
,int>(item
, offset
+ i
);
86 if (index
.FindOrAdd(ref p
))
87 throw new ArgumentException("Item already in indexed list");
95 /// Internal version of RemoveAt with no modification checks.
97 /// <param name="i">Index to remove at</param>
98 /// <returns>The removed item</returns>
99 protected override T
removeAt(int i
)
101 T val
= base.removeAt(i
);
103 index
.Remove(new KeyValuePair
<T
,int>(val
));
114 /// Create a hashed array list with the natural hasher
116 public HashedArrayList() : this(8) { }
120 /// Create a hashed array list with the natural hasher and specified capacity
122 /// <param name="cap">The initial capacity</param>
123 public HashedArrayList(int cap
) : this(cap
,HasherBuilder
.ByPrototype
<T
>.Examine()) { }
127 /// Create a hashed array list with an external hasher
129 /// <param name="hasher">The external hasher</param>
130 public HashedArrayList(IHasher
<T
> hasher
) : this(8,hasher
) { }
134 /// Create a hashed array list with an external hasher and specified capacity
136 /// <param name="capacity">The initial capacity</param>
137 /// <param name="hasher">The external hasher</param>
138 public HashedArrayList(int capacity
, IHasher
<T
> hasher
) : base(capacity
, hasher
)
140 index
= new HashSet
<KeyValuePair
<T
,int>>(new KeyValuePairHasher
<T
,int>(hasher
));
145 #region IList<T> Members
148 /// Insert into this list all items from an enumerable collection starting
149 /// at a particular index.
150 /// <exception cref="IndexOutOfRangeException"/> if i is negative or
151 /// > the size of the collection.
152 /// <exception cref="InvalidOperationException"/> if one of the items to insert is
153 /// already in the list.
155 /// <param name="i">Index to start inserting at</param>
156 /// <param name="items">Items to insert</param>
158 public override void InsertAll(int i
, MSG
.IEnumerable
<T
> items
)
161 if (i
< 0 || i
> size
)
162 throw new IndexOutOfRangeException();
167 KeyValuePair
<T
,int> p
= new KeyValuePair
<T
,int>();
169 foreach (T item
in items
)
173 if (!index
.FindOrAdd(ref p
)) j
++;
177 if (toadd
+ underlyingsize
> array
.Length
)
178 expand(toadd
+ underlyingsize
, underlyingsize
);
180 if (underlyingsize
> i
)
181 Array
.Copy(array
, i
, array
, j
, underlyingsize
- i
);
183 foreach (T item
in items
)
187 if (i
<= p
.value && p
.value < i
+ toadd
)
188 array
[p
.value] = item
;
195 internal override void InsertAll
<U
>(int i
, MSG
.IEnumerable
<U
> items
) //where U:T
198 if (i
< 0 || i
> size
)
199 throw new IndexOutOfRangeException();
204 KeyValuePair
<T
, int> p
= new KeyValuePair
<T
, int>();
206 foreach (T item
in items
)
210 if (!index
.FindOrAdd(ref p
)) j
++;
214 if (toadd
+ underlyingsize
> array
.Length
)
215 expand(toadd
+ underlyingsize
, underlyingsize
);
217 if (underlyingsize
> i
)
218 Array
.Copy(array
, i
, array
, j
, underlyingsize
- i
);
220 foreach (T item
in items
)
224 if (i
<= p
.value && p
.value < i
+ toadd
)
225 array
[p
.value] = item
;
233 /// Insert an item right before the first occurrence of some target item.
234 /// <exception cref="ArgumentException"/> if target is not found.
235 /// <exception cref="InvalidOperationException"/> if the item to insert is
236 /// already in the list.
238 /// <param name="item">The item to insert.</param>
239 /// <param name="target">The target before which to insert.</param>
241 public override void InsertBefore(T item
, T target
)
245 int ind
= indexOf(target
);
248 throw new ArgumentException("Target item not found");
255 /// Insert an item right after the last(???) occurrence of some target item.
256 /// <exception cref="ArgumentException"/> if target is not found.
257 /// <exception cref="InvalidOperationException"/> if the item to insert is
258 /// already in the list.
260 /// <param name="item">The item to insert.</param>
261 /// <param name="target">The target after which to insert.</param>
263 public override void InsertAfter(T item
, T target
)
267 int ind
= indexOf(target
);
270 throw new ArgumentException("Target item not found");
272 insert(ind
+ 1, item
);
277 /// Create a list view on this list.
278 /// <exception cref="ArgumentOutOfRangeException"/> if the view would not fit into
281 /// <param name="start">The index in this list of the start of the view.</param>
282 /// <param name="count">The size of the view.</param>
283 /// <returns>The new list view.</returns>
285 public override IList
<T
> View(int start
, int count
)
287 HashedArrayList
<T
> retval
= (HashedArrayList
<T
>)MemberwiseClone();
289 retval
.underlying
= underlying
!= null ? underlying
: this;
290 retval
.offset
= start
;
297 /// Reverst part of the list so the items are in the opposite sequence order.
298 /// <exception cref="ArgumentException"/> if the count is negative.
299 /// <exception cref="ArgumentOutOfRangeException"/> if the part does not fit
302 /// <param name="start">The index of the start of the part to reverse.</param>
303 /// <param name="count">The size of the part to reverse.</param>
305 public override void Reverse(int start
, int count
)
307 base.Reverse(start
, count
);
308 reindex(offset
+ start
, offset
+ start
+ count
);
313 /// Sort the items of the list according to a specific sorting order.
315 /// <param name="c">The comparer defining the sorting order.</param>
317 public override void Sort(IComparer
<T
> c
)
320 reindex(offset
, offset
+ size
);
324 /// Shuffle the items of this list according to a specific random source.
326 /// <param name="rnd">The random source.</param>
327 public override void Shuffle(Random rnd
)
330 reindex(offset
, offset
+ size
);
335 #region IIndexed<T> Members
339 /// Remove all items in an index interval.
340 /// <exception cref="IndexOutOfRangeException"/>???.
342 /// <param name="start">The index of the first item to remove.</param>
343 /// <param name="count">The number of items to remove.</param>
345 public override void RemoveInterval(int start
, int count
)
349 KeyValuePair
<T
,int> p
= new KeyValuePair
<T
,int>();
351 for (int i
= offset
+ start
, end
= offset
+ start
+ count
; i
< end
; i
++)
357 base.RemoveInterval(start
, count
);
358 reindex(offset
+ start
);
363 #region ISequenced<T> Members
365 int ISequenced
<T
>.GetHashCode()
366 { modifycheck(); return sequencedhashcode(); }
369 bool ISequenced
<T
>.Equals(ISequenced
<T
> that
)
370 { modifycheck(); return sequencedequals(that); }
375 #region IEditableCollection<T> Members
378 /// The value is symbolic indicating the type of asymptotic complexity
379 /// in terms of the size of this collection (expected).
381 /// <value>Speed.Constant</value>
383 public override Speed ContainsSpeed { [Tested]get { return Speed.Constant; }
}
387 /// Check if this collection contains (an item equivalent to according to the
388 /// itemhasher) a particular value.
390 /// <param name="item">The value to check for.</param>
391 /// <returns>True if the items is in this collection.</returns>
393 public override bool Contains(T item
)
394 { modifycheck(); return indexOf(item) >= 0; }
398 /// Remove the first copy of a particular item from this collection.
400 /// <param name="item">The value to remove.</param>
401 /// <returns>True if the item was found (and removed).</returns>
403 public override bool Remove(T item
)
407 int ind
= indexOf(item
);
418 /// Remove all items in another collection from this one, taking multiplicities into account.
419 /// Matching items will be removed from the front. Current implementation is not optimal.
421 /// <param name="items">The items to remove.</param>
423 public override void RemoveAll(MSG
.IEnumerable
<T
> items
)
427 KeyValuePair
<T
,int> p
= new KeyValuePair
<T
,int>();
429 foreach (T item
in items
)
432 if (index
.Find(ref p
) && offset
<= p
.value && p
.value < offset
+ size
)
436 int removed
= underlyingsize
- index
.Count
, j
= offset
;
438 for (int i
= offset
; i
< underlyingsize
; i
++)
442 if (index
.Update(p
)) { array[j++] = p.key; }
446 Array
.Clear(array
, underlyingsize
, removed
);
451 /// Remove all items from this collection, resetting internal array size.
454 public override void Clear()
457 if (underlying
== null)
464 for (int i
= offset
, end
= offset
+ size
; i
< end
; i
++)
465 index
.Remove(new KeyValuePair
<T
,int>(array
[i
]));
474 /// Remove all items not in some other collection from this one.
476 /// <param name="items">The items to retain.</param>
478 public override void RetainAll(MSG
.IEnumerable
<T
> items
)
482 HashSet
<T
> toretain
= new HashSet
<T
>(itemhasher
);
483 KeyValuePair
<T
,int> p
= new KeyValuePair
<T
,int>();
485 foreach (T item
in items
)
488 if (index
.Find(ref p
) && offset
<= p
.value && p
.value < offset
+ size
)
492 int removed
= size
- toretain
.Count
, j
= offset
;
494 for (int i
= offset
; i
< offset
+ size
; i
++)
498 if (toretain
.Contains(p
.key
))
509 Array
.Copy(array
, offset
+ size
, array
, j
, underlyingsize
- offset
- size
);
512 Array
.Clear(array
, underlyingsize
, removed
);
517 /// Check if this collection contains all the values in another collection.
519 /// <param name="items">The </param>
520 /// <returns>True if all values in <code>items</code>is in this collection.</returns>
522 public override bool ContainsAll(MSG
.IEnumerable
<T
> items
)
525 foreach (T item
in items
)
526 if (indexOf(item
) < 0)
534 /// Count the number of items of the collection equal to a particular value.
535 /// Returns 0 if and only if the value is not in the collection.
537 /// <param name="item">The value to count.</param>
538 /// <returns>The number of copies found (0 or 1).</returns>
540 public override int ContainsCount(T item
)
541 { modifycheck(); return indexOf(item) >= 0 ? 1 : 0; }
545 /// Remove all items equal to a given one.
547 /// <param name="item">The value to remove.</param>
549 public override void RemoveAllCopies(T item
) { Remove(item); }
553 /// Check the integrity of the internal data structures of this array list.
555 /// <returns>True if check does not fail.</returns>
557 public override bool Check()
564 if (underlyingsize
!= index
.Count
)
566 Console
.WriteLine("size ({0})!= index.Count ({1})", size
, index
.Count
);
570 for (int i
= 0; i
< underlyingsize
; i
++)
572 KeyValuePair
<T
,int> p
= new KeyValuePair
<T
,int>(array
[i
]);
574 if (!index
.Find(ref p
))
576 Console
.WriteLine("Item {1} at {0} not in hashindex", i
, array
[i
]);
582 Console
.WriteLine("Item {1} at {0} has hashindex {2}", i
, array
[i
], p
.value);
591 int ICollection
<T
>.GetHashCode() { return unsequencedhashcode(); }
594 bool ICollection
<T
>.Equals(ICollection
<T
> that
)
595 { return unsequencedequals(that); }
599 #region ISink<T> Members
604 /// <value>False, indicating hashed array list has set semantics.</value>
606 public override bool AllowsDuplicates { [Tested]get { return false; }
}
610 /// Add an item to end of this list if not already in list.
612 /// <param name="item">The item to add.</param>
613 /// <returns>True if item was added</returns>
615 public override bool Add(T item
)
619 KeyValuePair
<T
,int> p
= new KeyValuePair
<T
,int>(item
, size
);
621 if (index
.FindOrAdd(ref p
))
624 base.insert(size
, item
);
631 #region IDirectedEnumerable<T> Members
633 IDirectedEnumerable
<T
> IDirectedEnumerable
<T
>.Backwards() { return Backwards(); }