Unity UGUI原始碼——容器IndexedSet
List 和 Dictionary,是最常用的資料結構之二。
先來看看List 和 Dictionary的優缺點:
1.遍歷,List可以 for 可以 foreach 還可以.ForEach(),而 Dictionary只能foreach (Unity某些版本使用foreach會由於拆裝箱產生GC)。List的遍歷順序就是元素的新增順序,Dictionary是Hash方式儲存的,所以遍歷時無法保證順序。
2.List是支援排序的(key為固定的整數索引)。可以直接調Sort()、Reverse()等方法, Dictionary不支援(key型別不確定)。
3.List查詢慢,需要遍歷,時間複雜度是 O(n), Dictionary查詢快,使用雜湊查詢方式,時間複雜度為 O(1)。
4.List 移除效率低,內部由陣列實現並非連結串列。 Remove(item)需要先遍歷查詢到對應的index,RemoveAt(index)方法需要從在移除index處的元素後,後邊的元素依次向前補位。在List長度較長甚至同時需要多次移除時,對效能影響較大。
msdn Dictionary 原始碼地址
--------------------NRatel割--------------------
今天看UGUI原始碼時發現,Unity UnityEngine.UI.Collections 名稱空間下實現了一個IndexedSet類,比較巧妙地吸取了List 和 Dictionary各自的優點。當然也不是完美的)。
程式碼比較簡單。這裡我對它的註釋做一下解釋。
這是這樣的一個容器:
1.items 都是唯一的 (item必須唯一,因為要用item作為字典的key, 這樣帶來的好處是查詢變成了快速的hash查詢方式)。
2.快速隨機移除 (移除時Remove不需要再先做線性查找了。RemoveAt將要移除的元素與尾部元素換位,然後移除尾部,避免了後邊元素依次向前補位。這塊其實也打亂了原來的排序,不過問題不大,如果想要恢復排序。可以在所有移除進行完之後執行一次Sort)
3.Fast unique inclusion to the end (暫時沒明白這句話的意思, 知道的可以留言告訴我)
4.順序訪問(支援通過index查詢,支援有序,支援靈活的遍歷方式)
缺點:
1佔用更多的記憶體。 (沒辦法,本來只用存一份,現在要列表中一份,字典中一份)
2排序是非永久的。(移除操作為了提升效率,會打亂排序)
3不易序列化. (因為是組合的資料結構)
可以在符合上述條件的、恰當的情況下使用它。
由於它是 internal class,不能直接調它的實現,可以拷貝一份出來再用。
既然寫到這了,不妨再捋一捋C#的資料結構。
最後,貼上 IndexedSet 的原始碼,(純貼上自 Unity 2017.3.1f1)
using System; using System.Collections; using System.Collections.Generic; namespace UnityEngine.UI.Collections { internal class IndexedSet<T> : IList<T> { //This is a container that gives: // - Unique items // - Fast random removal // - Fast unique inclusion to the end // - Sequential access //Downsides: // - Uses more memory // - Ordering is not persistent // - Not Serialization Friendly. //We use a Dictionary to speed up list lookup, this makes it cheaper to guarantee no duplicates (set) //When removing we move the last item to the removed item position, this way we only need to update the index cache of a single item. (fast removal) //Order of the elements is not guaranteed. A removal will change the order of the items. readonly List<T> m_List = new List<T>(); Dictionary<T, int> m_Dictionary = new Dictionary<T, int>(); public void Add(T item) { if (m_Dictionary.ContainsKey(item)) return; m_List.Add(item); m_Dictionary.Add(item, m_List.Count - 1); } public bool Remove(T item) { int index = -1; if (!m_Dictionary.TryGetValue(item, out index)) return false; RemoveAt(index); return true; } public IEnumerator<T> GetEnumerator() { throw new System.NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Clear() { m_List.Clear(); m_Dictionary.Clear(); } public bool Contains(T item) { return m_Dictionary.ContainsKey(item); } public void CopyTo(T[] array, int arrayIndex) { m_List.CopyTo(array, arrayIndex); } public int Count { get { return m_List.Count; } } public bool IsReadOnly { get { return false; } } public int IndexOf(T item) { int index = -1; m_Dictionary.TryGetValue(item, out index); return index; } public void Insert(int index, T item) { //We could support this, but the semantics would be weird. Order is not guaranteed.. throw new NotSupportedException("Random Insertion is semantically invalid, since this structure does not guarantee ordering."); } public void RemoveAt(int index) { T item = m_List[index]; m_Dictionary.Remove(item); if (index == m_List.Count - 1) m_List.RemoveAt(index); else { int replaceItemIndex = m_List.Count - 1; T replaceItem = m_List[replaceItemIndex]; m_List[index] = replaceItem; m_Dictionary[replaceItem] = index; m_List.RemoveAt(replaceItemIndex); } } public T this[int index] { get { return m_List[index]; } set { T item = m_List[index]; m_Dictionary.Remove(item); m_List[index] = value; m_Dictionary.Add(item, index); } } public void RemoveAll(Predicate<T> match) { //I guess this could be optmized by instead of removing the items from the list immediatly, //We move them to the end, and then remove all in one go. //But I don't think this is going to be the bottleneck, so leaving as is for now. int i = 0; while (i < m_List.Count) { T item = m_List[i]; if (match(item)) Remove(item); else i++; } } //Sorts the internal list, this makes the exposed index accessor sorted as well. //But note that any insertion or deletion, can unorder the collection again. public void Sort(Comparison<T> sortLayoutFunction) { //There might be better ways to sort and keep the dictionary index up to date. m_List.Sort(sortLayoutFunction); //Rebuild the dictionary index. for (int i = 0; i < m_List.Count; ++i) { T item = m_List[i]; m_Dictionary[item] = i; } } } }
--------------------NRatel割--------------------
NRatel
轉載請說明出處,謝謝