source: trunk/sources/HeuristicLab.Collections/3.3/ObservableList.cs @ 10324

Last change on this file since 10324 was 10324, checked in by gkronber, 7 years ago

#2106: changed methods for sorting in ObservableArray and ObservableList to use a stable sort (via Enumerable.OrderBy()). This is implemented as extension methods in HeuristicLab.Common. This implementation requires additional memory O(n).
The unit tests for tabu search had to be updated as the stable sort changes the results of the sample.
(minor bug fix in TestRandom)

File size: 17.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2013 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections;
24using System.Collections.Generic;
25using System.ComponentModel;
26using System.Linq;
27using HeuristicLab.Common;
28using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
29
30namespace HeuristicLab.Collections {
31  [StorableClass]
32  [Serializable]
33  public class ObservableList<T> : IObservableList<T> {
34    [Storable]
35    protected List<T> list;
36
37    #region Properties
38    public int Capacity {
39      get { return list.Capacity; }
40      set {
41        if (list.Capacity != value) {
42          list.Capacity = value;
43          OnPropertyChanged("Capacity");
44        }
45      }
46    }
47    public int Count {
48      get { return list.Count; }
49    }
50    bool ICollection<T>.IsReadOnly {
51      get { return ((ICollection<T>)list).IsReadOnly; }
52    }
53
54    public T this[int index] {
55      get {
56        return list[index];
57      }
58      set {
59        T item = list[index];
60        if (!((item == null) && (value == null)) && ((item == null) || (!item.Equals(value)))) {
61          list[index] = value;
62          OnItemsReplaced(new IndexedItem<T>[] { new IndexedItem<T>(index, value) }, new IndexedItem<T>[] { new IndexedItem<T>(index, item) });
63          OnPropertyChanged("Item[]");
64        }
65      }
66    }
67    #endregion
68
69    #region Constructors
70    public ObservableList() {
71      list = new List<T>();
72    }
73    public ObservableList(int capacity) {
74      list = new List<T>(capacity);
75    }
76    public ObservableList(IEnumerable<T> collection) {
77      list = new List<T>(collection);
78    }
79    [StorableConstructor]
80    protected ObservableList(bool deserializing) { }
81    #endregion
82
83    #region Access
84    public List<T> GetRange(int index, int count) {
85      return list.GetRange(index, count);
86    }
87
88    public bool Contains(T item) {
89      return list.Contains(item);
90    }
91
92    public int IndexOf(T item) {
93      return list.IndexOf(item);
94    }
95    public int IndexOf(T item, int index) {
96      return list.IndexOf(item, index);
97    }
98    public int IndexOf(T item, int index, int count) {
99      return list.IndexOf(item, index, count);
100    }
101
102    public int LastIndexOf(T item) {
103      return list.LastIndexOf(item);
104    }
105    public int LastIndexOf(T item, int index) {
106      return list.LastIndexOf(item, index);
107    }
108    public int LastIndexOf(T item, int index, int count) {
109      return list.LastIndexOf(item, index, count);
110    }
111
112    public int BinarySearch(T item) {
113      return list.BinarySearch(item);
114    }
115    public int BinarySearch(T item, IComparer<T> comparer) {
116      return list.BinarySearch(item, comparer);
117    }
118    public int BinarySearch(int index, int count, T item, IComparer<T> comparer) {
119      return list.BinarySearch(index, count, item, comparer);
120    }
121
122    public bool Exists(Predicate<T> match) {
123      return list.Exists(match);
124    }
125
126    public T Find(Predicate<T> match) {
127      return list.Find(match);
128    }
129    public List<T> FindAll(Predicate<T> match) {
130      return list.FindAll(match);
131    }
132    public T FindLast(Predicate<T> match) {
133      return list.FindLast(match);
134    }
135
136    public int FindIndex(Predicate<T> match) {
137      return list.FindIndex(match);
138    }
139    public int FindIndex(int startIndex, Predicate<T> match) {
140      return list.FindIndex(startIndex, match);
141    }
142    public int FindIndex(int startIndex, int count, Predicate<T> match) {
143      return list.FindIndex(startIndex, count, match);
144    }
145
146    public int FindLastIndex(Predicate<T> match) {
147      return list.FindLastIndex(match);
148    }
149    public int FindLastIndex(int startIndex, Predicate<T> match) {
150      return list.FindLastIndex(startIndex, match);
151    }
152    public int FindLastIndex(int startIndex, int count, Predicate<T> match) {
153      return list.FindLastIndex(startIndex, count, match);
154    }
155    #endregion
156
157    #region Manipulation
158    public void Add(T item) {
159      int capacity = list.Capacity;
160      list.Add(item);
161      OnItemsAdded(new IndexedItem<T>[] { new IndexedItem<T>(list.Count - 1, item) });
162      OnItemsAdded(new T[] { item });
163      if (list.Capacity != capacity)
164        OnPropertyChanged("Capacity");
165      OnPropertyChanged("Item[]");
166      OnPropertyChanged("Count");
167    }
168    public void AddRange(IEnumerable<T> collection) {
169      int capacity = list.Capacity;
170      int index = list.Count;
171      list.AddRange(collection);
172      List<IndexedItem<T>> items = new List<IndexedItem<T>>();
173      foreach (T item in collection) {
174        items.Add(new IndexedItem<T>(index, item));
175        index++;
176      }
177      if (items.Count > 0) {
178        OnItemsAdded(items);
179        OnItemsAdded(collection);
180        if (list.Capacity != capacity)
181          OnPropertyChanged("Capacity");
182        OnPropertyChanged("Item[]");
183        OnPropertyChanged("Count");
184      }
185    }
186
187    public void Insert(int index, T item) {
188      int capacity = list.Capacity;
189      list.Insert(index, item);
190      OnItemsAdded(new IndexedItem<T>[] { new IndexedItem<T>(index, item) });
191      OnItemsAdded(new T[] { item });
192      if (list.Capacity != capacity)
193        OnPropertyChanged("Capacity");
194      OnPropertyChanged("Item[]");
195      OnPropertyChanged("Count");
196    }
197    public void InsertRange(int index, IEnumerable<T> collection) {
198      int capacity = list.Capacity;
199      list.InsertRange(index, collection);
200      List<IndexedItem<T>> items = new List<IndexedItem<T>>();
201      foreach (T item in collection) {
202        items.Add(new IndexedItem<T>(index, item));
203        index++;
204      }
205      if (items.Count > 0) {
206        OnItemsAdded(items);
207        OnItemsAdded(collection);
208        if (list.Capacity != capacity)
209          OnPropertyChanged("Capacity");
210        OnPropertyChanged("Item[]");
211        OnPropertyChanged("Count");
212      }
213    }
214
215    /// <summary>
216    /// Performs a Clear and an AddRange, but does not fire separate events for those operations
217    /// </summary>
218    /// <param name="collection"></param>
219    public void Replace(IEnumerable<T> collection) {
220      List<IndexedItem<T>> oldItems = null;
221      if (list.Any()) oldItems = list.Select((x, i) => new IndexedItem<T>(i, x)).ToList();
222      else oldItems = new List<IndexedItem<T>>();
223
224      int oldCapacity = list.Capacity;
225      list.Clear();
226      list.AddRange(collection);
227
228      List<IndexedItem<T>> items = null;
229      if (list.Any()) items = list.Select((x, i) => new IndexedItem<T>(i, x)).ToList();
230      else items = new List<IndexedItem<T>>();
231
232      OnItemsReplaced(items, oldItems);
233      if (oldCapacity != list.Capacity) OnPropertyChanged("Capacity");
234      OnPropertyChanged("Item[]");
235      if (oldItems.Count != items.Count) OnPropertyChanged("Count");
236    }
237
238    public bool Remove(T item) {
239      int index = list.IndexOf(item);
240      if (index != -1) {
241        list.RemoveAt(index);
242        OnItemsRemoved(new IndexedItem<T>[] { new IndexedItem<T>(index, item) });
243        OnItemsRemoved(new T[] { item });
244        OnPropertyChanged("Item[]");
245        OnPropertyChanged("Count");
246        return true;
247      }
248      return false;
249    }
250    public int RemoveAll(Predicate<T> match) {
251      if (match == null) throw new ArgumentNullException();
252      List<IndexedItem<T>> indexedItems = new List<IndexedItem<T>>();
253      List<T> items = new List<T>();
254      for (int i = 0; i < list.Count; i++) {
255        if (match(list[i])) {
256          indexedItems.Add(new IndexedItem<T>(i, list[i]));
257          items.Add(list[i]);
258        }
259      }
260      int result = 0;
261      if (indexedItems.Count > 0) {
262        result = list.RemoveAll(match);
263        OnItemsRemoved(indexedItems);
264        OnItemsRemoved(items);
265        OnPropertyChanged("Item[]");
266        OnPropertyChanged("Count");
267      }
268      return result;
269    }
270    public void RemoveAt(int index) {
271      T item = list[index];
272      list.RemoveAt(index);
273      OnItemsRemoved(new IndexedItem<T>[] { new IndexedItem<T>(index, item) });
274      OnItemsRemoved(new T[] { item });
275      OnPropertyChanged("Item[]");
276      OnPropertyChanged("Count");
277    }
278    public void RemoveRange(int index, int count) {
279      if (count > 0) {
280        IndexedItem<T>[] indexedItems = GetIndexedItems(index, count);
281        T[] items = new T[count];
282        list.CopyTo(index, items, 0, count);
283        list.RemoveRange(index, count);
284        OnItemsRemoved(indexedItems);
285        OnItemsRemoved(items);
286        OnPropertyChanged("Item[]");
287        OnPropertyChanged("Count");
288      }
289    }
290
291    public void Clear() {
292      if (list.Count > 0) {
293        IndexedItem<T>[] indexedItems = GetIndexedItems();
294        T[] items = list.ToArray();
295        list.Clear();
296        OnCollectionReset(new IndexedItem<T>[0], indexedItems);
297        OnCollectionReset(new T[0], items);
298        OnPropertyChanged("Item[]");
299        OnPropertyChanged("Count");
300      }
301    }
302
303    public void Reverse() {
304      if (list.Count > 1) {
305        IndexedItem<T>[] oldItems = GetIndexedItems();
306        list.Reverse();
307        OnItemsMoved(GetIndexedItems(), oldItems);
308        OnPropertyChanged("Item[]");
309      }
310    }
311    public void Reverse(int index, int count) {
312      if (count > 1) {
313        IndexedItem<T>[] oldItems = GetIndexedItems(index, count);
314        list.Reverse(index, count);
315        OnItemsMoved(GetIndexedItems(index, count), oldItems);
316        OnPropertyChanged("Item[]");
317      }
318    }
319
320    public void Sort() {
321      if (list.Count > 1) {
322        IndexedItem<T>[] oldItems = GetIndexedItems();
323        list.StableSort();
324        OnItemsMoved(GetIndexedItems(), oldItems);
325        OnPropertyChanged("Item[]");
326      }
327    }
328    public void Sort(Comparison<T> comparison) {
329      if (list.Count > 1) {
330        IndexedItem<T>[] oldItems = GetIndexedItems();
331        list.StableSort(comparison);
332        OnItemsMoved(GetIndexedItems(), oldItems);
333        OnPropertyChanged("Item[]");
334      }
335    }
336    public void Sort(IComparer<T> comparer) {
337      if (list.Count > 1) {
338        IndexedItem<T>[] oldItems = GetIndexedItems();
339        list.StableSort(comparer);
340        OnItemsMoved(GetIndexedItems(), oldItems);
341        OnPropertyChanged("Item[]");
342      }
343    }
344    public void Sort(int index, int count, IComparer<T> comparer) {
345      if (count > 1) {
346        IndexedItem<T>[] oldItems = GetIndexedItems(index, count);
347        list.StableSort(index, count, comparer);
348        OnItemsMoved(GetIndexedItems(index, count), oldItems);
349        OnPropertyChanged("Item[]");
350      }
351    }
352    #endregion
353
354    #region Conversion
355    public ReadOnlyObservableList<T> AsReadOnly() {
356      return new ReadOnlyObservableList<T>(this);
357    }
358
359    public T[] ToArray() {
360      return list.ToArray();
361    }
362    public void CopyTo(T[] array) {
363      list.CopyTo(array);
364    }
365    public void CopyTo(T[] array, int arrayIndex) {
366      list.CopyTo(array, arrayIndex);
367    }
368    public void CopyTo(int index, T[] array, int arrayIndex, int count) {
369      list.CopyTo(index, array, arrayIndex, count);
370    }
371    public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter) {
372      return list.ConvertAll<TOutput>(converter);
373    }
374    #endregion
375
376    #region Processing
377    public void ForEach(Action<T> action) {
378      list.ForEach(action);
379    }
380    public bool TrueForAll(Predicate<T> match) {
381      return list.TrueForAll(match);
382    }
383    #endregion
384
385    #region Enumeration
386    public IEnumerator<T> GetEnumerator() {
387      return list.GetEnumerator();
388    }
389    IEnumerator IEnumerable.GetEnumerator() {
390      return list.GetEnumerator();
391    }
392    #endregion
393
394    #region Helpers
395    public void TrimExcess() {
396      int capacity = list.Capacity;
397      list.TrimExcess();
398      if (list.Capacity != capacity)
399        OnPropertyChanged("Capacity");
400    }
401    #endregion
402
403    #region Events
404    [field: NonSerialized]
405    public event CollectionItemsChangedEventHandler<IndexedItem<T>> ItemsAdded;
406    protected virtual void OnItemsAdded(IEnumerable<IndexedItem<T>> items) {
407      CollectionItemsChangedEventHandler<IndexedItem<T>> handler = ItemsAdded;
408      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items));
409    }
410
411    [field: NonSerialized]
412    private event CollectionItemsChangedEventHandler<T> itemsAdded;
413    event CollectionItemsChangedEventHandler<T> INotifyObservableCollectionItemsChanged<T>.ItemsAdded {
414      add { itemsAdded += value; }
415      remove { itemsAdded -= value; }
416    }
417    private void OnItemsAdded(IEnumerable<T> items) {
418      CollectionItemsChangedEventHandler<T> handler = itemsAdded;
419      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<T>(items));
420    }
421
422    [field: NonSerialized]
423    public event CollectionItemsChangedEventHandler<IndexedItem<T>> ItemsRemoved;
424    protected virtual void OnItemsRemoved(IEnumerable<IndexedItem<T>> items) {
425      CollectionItemsChangedEventHandler<IndexedItem<T>> handler = ItemsRemoved;
426      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items));
427    }
428
429    [field: NonSerialized]
430    private event CollectionItemsChangedEventHandler<T> itemsRemoved;
431    event CollectionItemsChangedEventHandler<T> INotifyObservableCollectionItemsChanged<T>.ItemsRemoved {
432      add { itemsRemoved += value; }
433      remove { itemsRemoved -= value; }
434    }
435    private void OnItemsRemoved(IEnumerable<T> items) {
436      CollectionItemsChangedEventHandler<T> handler = itemsRemoved;
437      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<T>(items));
438    }
439
440    [field: NonSerialized]
441    public event CollectionItemsChangedEventHandler<IndexedItem<T>> ItemsReplaced;
442    protected virtual void OnItemsReplaced(IEnumerable<IndexedItem<T>> items, IEnumerable<IndexedItem<T>> oldItems) {
443      CollectionItemsChangedEventHandler<IndexedItem<T>> handler = ItemsReplaced;
444      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items, oldItems));
445    }
446
447    [field: NonSerialized]
448    public event CollectionItemsChangedEventHandler<IndexedItem<T>> ItemsMoved;
449    protected virtual void OnItemsMoved(IEnumerable<IndexedItem<T>> items, IEnumerable<IndexedItem<T>> oldItems) {
450      CollectionItemsChangedEventHandler<IndexedItem<T>> handler = ItemsMoved;
451      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items, oldItems));
452    }
453
454    [field: NonSerialized]
455    public event CollectionItemsChangedEventHandler<IndexedItem<T>> CollectionReset;
456    protected virtual void OnCollectionReset(IEnumerable<IndexedItem<T>> items, IEnumerable<IndexedItem<T>> oldItems) {
457      CollectionItemsChangedEventHandler<IndexedItem<T>> handler = CollectionReset;
458      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items, oldItems));
459    }
460
461    [field: NonSerialized]
462    private event CollectionItemsChangedEventHandler<T> collectionReset;
463    event CollectionItemsChangedEventHandler<T> INotifyObservableCollectionItemsChanged<T>.CollectionReset {
464      add { collectionReset += value; }
465      remove { collectionReset -= value; }
466    }
467    private void OnCollectionReset(IEnumerable<T> items, IEnumerable<T> oldItems) {
468      CollectionItemsChangedEventHandler<T> handler = collectionReset;
469      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<T>(items, oldItems));
470    }
471
472    [field: NonSerialized]
473    public event PropertyChangedEventHandler PropertyChanged;
474    protected virtual void OnPropertyChanged(string propertyName) {
475      PropertyChangedEventHandler handler = PropertyChanged;
476      if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
477    }
478    #endregion
479
480    #region Private helpers
481    private IndexedItem<T>[] GetIndexedItems() {
482      IndexedItem<T>[] items = new IndexedItem<T>[list.Count];
483      for (int i = 0; i < list.Count; i++)
484        items[i] = new IndexedItem<T>(i, list[i]);
485      return items;
486    }
487    private IndexedItem<T>[] GetIndexedItems(int index, int count) {
488      IndexedItem<T>[] items = new IndexedItem<T>[count];
489      for (int i = 0; i < count; i++)
490        items[i] = new IndexedItem<T>(index + i, list[index + i]);
491      return items;
492    }
493    #endregion
494  }
495}
Note: See TracBrowser for help on using the repository browser.