前言
此些天来,武疫兴然,吾甚惧焉,居家不出,项目做完,游戏玩腻,根亦秃噜,百无聊赖,猛然惊醒,吾当面试,基础犹菜,从何下手,唯有C#,C#之何,集合为多,多而不晓,遂有此文。
环境
.Net Framework 4.8
前置知识
Hash
Hash算法无论是在项目开发还是底层语言设计上都是非常常见的一个算法,既然如此常用,那么它的重要性自然不必多说,这也是把它放在第一位讲的原因。
Hash算法与Hash函数
Hash算法
是一种数字摘要算法,它能将不定长度
的二进制数据集给映射到一个较短
的二进制长度数据集。常见的MD5算法就是一种Hash算法,通过MD5算法可对任何数据生成数字摘要。而实现
了Hash算法的函数我们叫它Hash函数
。Hash函数有以下几点特征。
相同的数据进行Hash运算,得到的结果一定相同。
不同的数据进行Hash运算,其结果也可能会相同。
Hash运算是不可逆的,不能由key获取原始的数据。
常见的Hash算法
直接寻址法:取keyword或keyword的某个线性函数值为散列地址。即H(key)=key或H(key) = a•key + b,当中a和b为常数(这样的散列函数叫做自身函数)
平方取中法:取keyword平方后的中间几位作为散列地址。
折叠法:将keyword切割成位数同样的几部分,最后一部分位数能够不同,然后取这几部分的叠加和(去除进位)作为散列地址。
随机数法:选择一随机函数,取keyword的随机值作为散列地址,通经常使用于keyword长度不同的场合。
除留余数法:取keyword被某个不大于散列表表长m的数p除后所得的余数为散列地址。即 H(key) = key MOD p, p<=m。不仅能够对keyword直接取模,也可在折叠、平方取中等运算之后取模。对p的选择非常重要,一般取素数或m,若p选的不好,容易产生碰撞。
Hash桶算法
Hash桶算法主要是用来归类Hash值的,一般用来配合其他算法计算最终Hash,而不是单独用的,不然Hash碰撞会非常频繁。(其实就是除留余数法的最简化版。)
Hash碰撞
上面的Hash函数特征介绍中有一条:不同的数据进行Hash运算,其结果也可能会相同。
。这就是Hash碰撞,即不同的数据经过Hash函数处理后得到了相同的输出
。这并不是我们所期望看到的,所以为了解决Hash碰撞,又产生了很多解决Hash碰撞的方法。
常见解决Hash碰撞的方法
拉链法:这种方法的思路是将产生冲突的元素建立一个单链表,并将头指针地址存储至Hash表对应桶
的位置。这样定位到Hash表桶的位置后可通过遍历单链表的形式来查找元素。其实这种方法还是有瑕疵的,例如如果字典内所有键都在一个桶的链表里,那么查找起来时间复杂度还是O ( n ) O(n) O ( n ) 。在Java的HashMap(对位C#的字典)中,当单链表的长度大于8时,会把它变成红黑树
,优化增删改查效率。但是在.Net Framework 8里面暂时还没有哦
。
再Hash法:顾名思义就是将key使用其它的Hash函数再次Hash,直到找到不冲突的位置为止。
C#集合相关的原生类
我们在看C#源码的时候经常能看到一些接口,例如:IDictionary,ICollection,IEnumerable,IEnumerator,IComparer等,其中有些我们在自定义集合类型的时候也能用到,所以总的来说还是比较重要的,所以这里全都列举出来。
IEnumerable:如果需要自定义的类型支持foreach语义,就需要继承这个接口。有泛型版本(支持协变)。
IEnumerator:所有泛型计数器(我习惯叫他们迭代器)的基类,提供了一个方法,能够迭代到集合当前的元素。有泛型版本(支持协变)。
IComparer:用以对比两个对象x,y,如果x>y返回一个大于0的整数,如果x<y返回一个小于0的整数,如果x=y返回一个等于0的整数。会被Sort和BinarySearch调用。
IEqualityComparer:通常用于对比集合内部两个对象是否相等。返回一个bool变量。有泛型版本(支持逆变)。
ICollection:继承自IEnumerable。所有集合类型的基础接口。定义迭代器,大小,以及用于同步的方法。有泛型版本。
KeyValuePairs:键值对类。有泛型版本。其中的TKey和TValue可以为任意类型。
IList:所有泛型集合的基类。有泛型版本。
IDictionary:所有泛型字典基础接口。有泛型版本。
起飞
List
概述
List是常用的集合类型,他保证了数据的有序性,也就是说放进去的时候是什么顺序,不对List进行更改的话拿出来还是什么顺序。如果容量达到上限,还会自动扩容。
相关字段
1 2 3 4 private static readonly T[] s_emptyArray = new T[0 ];private T[] _items;private int _size;private int _version;
默认构造函数
1 2 3 4 public List ( ) { this ._items = List<T>.s_emptyArray; }
增加元素
将给定的对象添加到此列表的末尾。列表的大小增加了一个。在必要的情况下,列表的容量将在添加新元素之前增加一倍。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void Add (T item ) { if (_size == _items.Length) EnsureCapacity(_size + 1 ); _items[_size++] = item; _version++; } private void EnsureCapacity (int min ) { if (_items.Length < min) { int newCapacity = _items.Length == 0 ? _defaultCapacity : _items.Length * 2 ; if ((uint) newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength; if (newCapacity < min) newCapacity = min; Capacity = newCapacity; } }
删除元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public bool Remove (T item ) { int index = IndexOf(item); if (index >= 0 ) { RemoveAt(index); return true ; } return false ; } public void RemoveAt (int index ) { if ((uint) index >= (uint) _size) { } _size--; if (index < _size) { Array.Copy(_items, index + 1 , _items, index, _size - index); } _items[_size] = default (T); _version++; }
查找元素
在我们对List调用foreach的时候,会执行List的GetEnumerator方法得到新的迭代器。然后会进行版本号对比
,一旦在遍历过程中对List做了增删操作
,版本号就会变化,从而与迭代器的版本号不一致,我们也就会看到报错异常
。所以不能在遍历过程中对List进行增删操作。会这样设计是有原因的,因为一旦允许在遍历过程中进行增删操作,遍历就不再是遍历了,要么会重复,要么会缺漏,再严重点就直接空指针了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public Enumerator GetEnumerator ( ) { return new Enumerator(this ); } internal Enumerator (List<T> list ) { this .list = list; index = 0 ; version = list._version; current = default (T); } private bool MoveNextRare ( ) { if (version != list._version) { } index = list._size + 1 ; current = default (T); return false ; }
Dictionary
概述
字典相对于集合就复杂的多了,因为涉及到了Hash碰撞。
需要注意的是,HashTable是线程安全的,而字典不是。
其实字典这一块已经有前辈做了更加详尽的分析:https://www.cnblogs.com/InCerry/p/10325290.html ,我在这里只是再粗略的按自己的理解概括一遍,大家自行斟酌。
相关字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private struct Entry{ public int hashCode; public int next; public TKey key; public TValue value ; } private int [] buckets; private Entry[] entries;private int count; private int version; private int freeList; private int freeCount; private IEqualityComparer<TKey> comparer; private KeyCollection keys; private ValueCollection values;
增加元素
当Hash桶内发生碰撞时,会变成单链表。当entries数组满的时候,字典会进行扩容,也就是数组拷贝,最好提前设定好字典容量,避免频繁的字典扩容。
另外这里为了方便讲解,省略了Hash碰撞次数统计。Hash碰撞次数到达一个阈值,也会触发扩容操作
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 private void Insert (TKey key, TValue value , bool add ) { if (key == null ) { } if (buckets == null ) Initialize(0 ); int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF ; int targetBucket = hashCode % buckets.Length; for (int i = buckets[targetBucket]; i >= 0 ; i = entries[i].next) { if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) { if (add ) { } entries[i].value = value ; version++; return ; } } int index; if (freeCount > 0 ) { index = freeList; freeList = entries[index].next; freeCount--; } else { if (count == entries.Length) { Resize(); targetBucket = hashCode % buckets.Length; } index = count; count++; } entries[index].hashCode = hashCode; entries[index].next = buckets[targetBucket]; entries[index].key = key; entries[index].value = value ; buckets[targetBucket] = index; version++; }
删除元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public bool Remove (TKey key ) { if (key == null ) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); } if (buckets != null ) { int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF ; int bucket = hashCode % buckets.Length; int last = -1 ; for (int i = buckets[bucket]; i >= 0 ; last = i, i = entries[i].next) { if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) { if (last < 0 ) { buckets[bucket] = entries[i].next; } else { entries[last].next = entries[i].next; } entries[i].hashCode = -1 ; entries[i].next = freeList; entries[i].key = default (TKey); entries[i].value = default (TValue); freeList = i; freeCount++; version++; return true ; } } } return false ; }
查找元素
同样是不能在遍历过程中对字典进行修改
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public bool MoveNext ( ) { if (version != dictionary.version) { } while ((uint) index < (uint) dictionary.count) { if (dictionary.entries[index].hashCode >= 0 ) { current = new KeyValuePair<TKey, TValue>(dictionary.entries[index].key, dictionary.entries[index].value ); index++; return true ; } index++; } index = dictionary.count + 1 ; current = new KeyValuePair<TKey, TValue>(); return false ; }
SortedDictionary
概述
SortedDictionary相对于Dictionary来说它是有序的,并且里面用到了红黑树
优化查找速率。
红黑树(RB-Tree)
的查找时间复杂度其实比自平衡二叉搜索树(AVL)
要高一些,内存占用也和AVL差不多,它的真正优势在于插入删除的效率高。
对于红黑树,大家可以去https://zhuanlan.zhihu.com/p/95892351 学习,自认为是比较好的教程了。
但是实际上由于排序的存在,它的各项性能其实并不如Dictionary
1 2 3 private KeyCollection keys;private ValueCollection values;private TreeSet<KeyValuePair<TKey, TValue>> _set;
增加元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 internal virtual bool AddIfNotPresent (T item ) { if (root == null ) { root = new Node(item, false ); count = 1 ; version++; return true ; } Node current = root; Node parent = null ; Node grandParent = null ; Node greatGrandParent = null ; version++; int order = 0 ; while (current != null ) { order = comparer.Compare(item, current.Item); if (order == 0 ) { root.IsRed = false ; return false ; } if (Is4Node(current)) { Split4Node(current); if (IsRed(parent)) { InsertionBalance(current, ref parent, grandParent, greatGrandParent); } } greatGrandParent = grandParent; grandParent = parent; parent = current; current = (order < 0 ) ? current.Left : current.Right; } Debug.Assert(parent != null , "Parent node cannot be null here!" ); Node node = new Node(item); if (order > 0 ) { parent.Right = node; } else { parent.Left = node; } if (parent.IsRed) { InsertionBalance(node, ref parent, grandParent, greatGrandParent); } root.IsRed = false ; ++count; return true ; } private void InsertionBalance (Node current, ref Node parent, Node grandParent, Node greatGrandParent ) { Debug.Assert(grandParent != null , "Grand parent cannot be null here!" ); bool parentIsOnRight = (grandParent.Right == parent); bool currentIsOnRight = (parent.Right == current); Node newChildOfGreatGrandParent; if (parentIsOnRight == currentIsOnRight) { newChildOfGreatGrandParent = currentIsOnRight ? RotateLeft(grandParent) : RotateRight(grandParent); } else { newChildOfGreatGrandParent = currentIsOnRight ? RotateLeftRight(grandParent) : RotateRightLeft(grandParent); parent = greatGrandParent; } grandParent.IsRed = true ; newChildOfGreatGrandParent.IsRed = false ; ReplaceChildOfNodeOrRoot(greatGrandParent, grandParent, newChildOfGreatGrandParent); }
删除元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 internal virtual bool DoRemove (T item ) { if (root == null ) { return false ; } version++; Node current = root; Node parent = null ; Node grandParent = null ; Node match = null ; Node parentOfMatch = null ; bool foundMatch = false ; while (current != null ) { if (Is2Node(current)) { if (parent == null ) { current.IsRed = true ; } else { Node sibling = GetSibling(current, parent); if (sibling.IsRed) { Debug.Assert(!parent.IsRed, "parent must be a black node!" ); if (parent.Right == sibling) { RotateLeft(parent); } else { RotateRight(parent); } parent.IsRed = true ; sibling.IsRed = false ; ReplaceChildOfNodeOrRoot(grandParent, parent, sibling); grandParent = sibling; if (parent == match) { parentOfMatch = sibling; } sibling = (parent.Left == current) ? parent.Right : parent.Left; } Debug.Assert(sibling != null || sibling.IsRed == false , "sibling must not be null and it must be black!" ); if (Is2Node(sibling)) { Merge2Nodes(parent, current, sibling); } else { TreeRotation rotation = RotationNeeded(parent, current, sibling); Node newGrandParent = null ; switch (rotation) { case TreeRotation.RightRotation: Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!" ); Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!" ); sibling.Left.IsRed = false ; newGrandParent = RotateRight(parent); break ; case TreeRotation.LeftRotation: Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!" ); Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!" ); sibling.Right.IsRed = false ; newGrandParent = RotateLeft(parent); break ; case TreeRotation.RightLeftRotation: Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!" ); Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!" ); newGrandParent = RotateRightLeft(parent); break ; case TreeRotation.LeftRightRotation: Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!" ); Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!" ); newGrandParent = RotateLeftRight(parent); break ; } newGrandParent.IsRed = parent.IsRed; parent.IsRed = false ; current.IsRed = true ; ReplaceChildOfNodeOrRoot(grandParent, parent, newGrandParent); if (parent == match) { parentOfMatch = newGrandParent; } grandParent = newGrandParent; } } } int order = foundMatch ? -1 : comparer.Compare(item, current.Item); if (order == 0 ) { foundMatch = true ; match = current; parentOfMatch = parent; } grandParent = parent; parent = current; if (order < 0 ) { current = current.Left; } else { current = current.Right; } } if (match != null ) { ReplaceNode(match, parentOfMatch, parent, grandParent); --count; } if (root != null ) { root.IsRed = false ; } return foundMatch; }
查找元素
它的迭代器的遍历设置的非常之巧妙。你细品。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 private void Intialize ( ) { current = null ; SortedSet<T>.Node node = tree.root; Node next = null , other = null ; while (node != null ) { next = (reverse ? node.Right : node.Left); other = (reverse ? node.Left : node.Right); if (tree.IsWithinRange(node.Item)) { stack.Push(node); node = next; } else if (next == null || !tree.IsWithinRange(next.Item)) { node = other; } else { node = next; } } } public bool MoveNext ( ) { tree.VersionCheck(); if (version != tree.version) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); } if (stack.Count == 0 ) { current = null ; return false ; } current = stack.Pop(); SortedSet<T>.Node node = (reverse ? current.Left : current.Right); Node next = null , other = null ; while (node != null ) { next = (reverse ? node.Right : node.Left); other = (reverse ? node.Left : node.Right); if (tree.IsWithinRange(node.Item)) { stack.Push(node); node = next; } else if (other == null || !tree.IsWithinRange(other.Item)) { node = next; } else { node = other; } } return true ; }
SortedList
概述
SortedList内部是使用数组实现的,添加和删除元素的时间复杂度是O(n),查找元素的时间复杂度是O(l o g n log_n l o g n )。是典型的用时间换空间。
SortedList和SortedDictionary同时支持快速查询和排序,SortedList优势在于使用的内存比 SortedDictionary 少;但是SortedDictionary可对未排序的数据执行更快的插入和删除操作:它的时间复杂度为 O(l o g n log_n l o g n ),而 SortedList为 O(n)。所以SortedList适用于既需要快速查找又需要顺序排列但是添加和删除元素较少的场景。
他两个的对比测试我也做了,只是。。。SortedList在插入1000w数据的时候我等了10来分钟都没好。。。所以也没有测下去的必要了。
相关字段
1 2 3 4 5 6 7 8 9 10 11 12 private TKey[] keys;private TValue[] values;private int _size;private int version;private IComparer<TKey> comparer;private KeyList keyList;private ValueList valueList;static TKey[] emptyKeys = new TKey[0 ];static TValue[] emptyValues = new TValue[0 ];private const int _defaultCapacity = 4 ;
增加元素
基本上和List一致,只是要处理两个数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private void Insert (int index, TKey key, TValue value ) { if (_size == keys.Length) EnsureCapacity(_size + 1 ); if (index < _size) { Array.Copy(keys, index, keys, index + 1 , _size - index); Array.Copy(values, index, values, index + 1 , _size - index); } keys[index] = key; values[index] = value ; _size++; version++; }
删除元素
基本上和List一致,只是要处理两个数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void RemoveAt (int index ) { if (index < 0 || index >= _size) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index); _size--; if (index < _size) { Array.Copy(keys, index + 1 , keys, index, _size - index); Array.Copy(values, index + 1 , values, index, _size - index); } keys[_size] = default (TKey); values[_size] = default (TValue); version++; }
查找元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public bool MoveNext ( ) { if (version != _sortedList.version) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); if ((uint) index < (uint) _sortedList.Count) { key = _sortedList.keys[index]; value = _sortedList.values[index]; index++; return true ; } index = _sortedList.Count + 1 ; key = default (TKey); value = default (TValue); return false ; }
Stack
概述
栈其实也是比较常用的一个数据结构了,它的实现要简单很多。内部实现还是数组,所以Push的时候时间复杂度可能为O(n),因为可能会发生扩容行为,Pop的话就是O(1)。
相关字段
1 2 3 4 5 6 private T[] _array;private int _size;private int _version;private const int _defaultCapacity = 4 ;static T[] _emptyArray = new T[0 ];
增加元素
1 2 3 4 5 6 7 8 9 10 11 12 13 public void Push (T item ) { if (_size == _array.Length) { T[] newArray = new T[(_array.Length == 0 ) ? _defaultCapacity : 2 * _array.Length]; Array.Copy(_array, 0 , newArray, 0 , _size); _array = newArray; } _array[_size++] = item; _version++; }
删除元素
1 2 3 4 5 6 7 8 9 10 11 12 13 public T Pop ( ) { if (_size == 0 ) { } _version++; T item = _array[--_size]; _array[_size] = default (T); return item; }
查找元素
反倒是它的迭代器这一块设计的倒是挺有意思。
注意同样不能遍历的时候对栈进行增删操作。
先来看他的迭代器构造函数
1 2 3 4 5 6 7 internal Enumerator (Stack<T> stack ) { _stack = stack; _version = _stack._version; _index = -2 ; currentElement = default (T); }
注意到那个index设置为了-2,这里是有特殊含义的,如果index为-2,代表这个迭代器是新生的,或者说是被重置的,迭代开始。如果为-1,说明迭代器已经由于某种原因被终止了,迭代结束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public bool MoveNext ( ) { bool retval; if (_version != _stack._version) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); if (_index == -2 ) { _index = _stack._size - 1 ; retval = (_index >= 0 ); if (retval) currentElement = _stack._array[_index]; return retval; } if (_index == -1 ) { return false ; } retval = (--_index >= 0 ); if (retval) currentElement = _stack._array[_index]; else currentElement = default (T); return retval; }
Queue
概述
队列内部实现使用了环形队列算法
,相对于传统的顺序队列,它具有更强的空间可复用性以及安全性。它的入队时间复杂度为O(n),出队时间复杂度为O(1)。
环形队列算法可以参考:https://www.jianshu.com/p/f93d0449e410
相关字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private T[] _array;private int _head;private int _tail;private int _size;private int _version;private const int _MinimumGrow = 4 ;private const int _ShrinkThreshold = 32 ;private const int _GrowFactor = 200 ;private const int _DefaultCapacity = 4 ;static T[] _emptyArray = new T[0 ];
增加元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public void Enqueue (T item ) { if (_size == _array.Length) { int newcapacity = (int ) ((long ) _array.Length * (long ) _GrowFactor / 100 ); if (newcapacity < _array.Length + _MinimumGrow) { newcapacity = _array.Length + _MinimumGrow; } SetCapacity(newcapacity); } _array[_tail] = item; _tail = (_tail + 1 ) % _array.Length; _size++; _version++; }
_head
一直确保指向队首。
然后这里的扩容就有意思了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 private void SetCapacity (int capacity ) { T[] newarray = new T[capacity]; if (_size > 0 ) { if (_head < _tail) { Array.Copy(_array, _head, newarray, 0 , _size); } else { Array.Copy(_array, _head, newarray, 0 , _array.Length - _head); Array.Copy(_array, 0 , newarray, _array.Length - _head, _tail); } } _array = newarray; _head = 0 ; _tail = (_size == capacity) ? 0 : _size; _version++; }
!{环形队列队尾的值小于队首的值情形}(https://myfirstblog.oss-cn-hangzhou.aliyuncs.com/2020/01/QQ截图20200128204059.png!webp )
删除元素
1 2 3 4 5 6 7 8 9 10 11 12 13 public T Dequeue ( ) { if (_size == 0 ) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyQueue); T removed = _array[_head]; _array[_head] = default (T); _head = (_head + 1 ) % _array.Length; _size--; _version++; return removed; }
查找元素
队列的查找部分和栈的设计一样,但是具体的数值意义是相反的,如果index为-1,代表这个迭代器是新生的,或者说是被重置的,迭代开始。如果为-2,说明迭代器已经由于某种原因被终止了,迭代结束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public bool MoveNext ( ) { if (_version != _q._version) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); if (_index == -2 ) return false ; _index++; if (_index == _q._size) { _index = -2 ; _currentElement = default (T); return false ; } _currentElement = _q.GetElement(_index); return true ; }
LinkedList
概述
LinkedList就是普通的单链表了,内部实现也是直接用的类似C++的方式,并没什么好说的。
HashSet
概述
HashSet就是List的无序版本,查找删除之类的操作性能要比List强,因为内部实现和Dictionary一样用到了Hash桶和单链表。
其他
还有很多不是很常用的集合,比如OrderedDictionary,ListDictionary等就不在这一一列举了,如果大家有需要可以自行去官网 搜索定义。
参考文献
https://www.cnblogs.com/InCerry/p/10325290.html
https://www.cnblogs.com/songwenjie/p/9185790.html
https://zhuanlan.zhihu.com/p/95892351