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 List 源码地址
msdn Dictionary 源码地址
--------------------NRatel割--------------------
IndexedSet 在 Unity UnityEngine.UI.Collections 命名空间下。
它比较巧妙地吸取了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,不能直接调它的实现,可以拷贝一份出来再用。
最后,贴上 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
转载请说明出处,谢谢