c#系列 list詳情
這里以list為介紹:
private static readonly T[] s_emptyArray = new T[0];
public List()
{
this._items = List<T>.s_emptyArray;
}
list本質(zhì)是一個(gè)數(shù)組。
同樣我們可以指定容量,如果我們知道了我們大概需要多少數(shù)據(jù),那么我們可以指定一下,這樣避免了resize的損耗。
就跟我們操作系統(tǒng)一樣,提前申請(qǐng)內(nèi)存大小。所以我們程序一般都有一個(gè)申請(qǐng)內(nèi)存,實(shí)際使用內(nèi)存,內(nèi)存碎片這幾個(gè)概念。
添加也是很簡(jiǎn)單哈
public void Add(T item)
{
++this._version;
T[] items = this._items;
int size = this._size;
if ((uint) size < (uint) items.Length)
{
this._size = size + 1;
items[size] = item;
}
else
this.AddWithResize(item);
}
判斷是否滿了,如果沒(méi)滿直接存到數(shù)組里面去,如果滿了,那么resize一下。
看下resize:
private void AddWithResize(T item)
{
int size = this._size;
this.EnsureCapacity(size + 1);
this._size = size + 1;
this._items[size] = item;
}
然后看一下擴(kuò)容步驟:
private void EnsureCapacity(int min)
{
if (this._items.Length >= min)
return;
int num = this._items.Length == 0 ? 4 : this._items.Length * 2;
if ((uint) num > 2146435071U)
num = 2146435071;
if (num < min)
num = min;
this.Capacity = num;
}
首先在做了一次判斷,判斷是否容量夠用,所以是size+1。
if (this._items.Length >= min) return;
這里就有人問(wèn)了外面不是判斷了,為什么里面還有判斷。
這個(gè)就是一些人喜歡談性能的地方了,認(rèn)為多此一舉,如果里面不判斷那么就不是一個(gè)成熟的方法,提現(xiàn)不出方法的封閉性,因?yàn)榉椒ǖ淖饔檬侵蛥?shù)打交道,外面是什么其實(shí)是不管的。
那么可以看出,一開(kāi)始是4,然后后面就是翻倍了。
然后重點(diǎn)看下:
this.Capacity = num;
這個(gè)this.Capacity 并不是普通的變量,而是一個(gè)屬性哈,不然你都納悶它是怎么擴(kuò)容了。
public int Capacity
{
get => _items.Length;
set
{
if (value < _size)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
}
if (value != _items.Length)
{
if (value > 0)
{
T[] newItems = new T[value];
if (_size > 0)
{
Array.Copy(_items, newItems, _size);
}
_items = newItems;
}
else
{
_items = s_emptyArray;
}
}
}
}
首先判斷了不能縮容,如果縮容直接異常,其次我們注意道這個(gè)Capacity 是piblic的,也就是說(shuō)我們?cè)谕獠烤涂梢灾苯诱{(diào)用。
后面邏輯就很簡(jiǎn)單創(chuàng)建一個(gè)新的數(shù)組,然后復(fù)制就ok了,然后重新賦值_items。
那么來(lái)看一下remove吧:
public bool Remove(T item)
{
int index = IndexOf(item);
if (index >= 0)
{
RemoveAt(index);
return true;
}
return false;
}
首先是找到其位置:
public int IndexOf(T item)
=> Array.IndexOf(_items, item, 0, _size);
int IList.IndexOf(object? item)
{
if (IsCompatibleObject(item))
{
return IndexOf((T)item!);
}
return -1;
}
可以看一下這個(gè)IsCompatibleObject,還是很有趣的。
private static bool IsCompatibleObject(object? value)
{
// Non-null values are fine. Only accept nulls if T is a class or Nullable<U>.
// Note that default(T) is not equal to null for value types except when T is Nullable<U>.
return (value is T) || (value == null && default(T) == null);
}
從這個(gè)說(shuō)明,其實(shí)我們是可以傳空對(duì)象的。
static void Main(string[] args)
{
List<object> lists = new List<object>();
lists.Add(null);
Console.WriteLine(lists.Count);
lists.Remove(null);
Console.ReadLine();
}

那么來(lái)看一下removeat吧:
public void RemoveAt(int index)
{
if ((uint)index >= (uint)_size)
{
ThrowHelper.ThrowArgumentOutOfRange_IndexException();
}
_size--;
if (index < _size)
{
Array.Copy(_items, index + 1, _items, index, _size - index);
}
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
_items[_size] = default!;
}
_version++;
}
這里可以看出list的remove操作還是性能損耗很大的,尤其是大的list。
這里有沒(méi)有注意道一個(gè)_version,這個(gè)有什么作用呢?
當(dāng)遍歷的時(shí)候我們就用的到
internal Enumerator(List<T> list)
{
_list = list;
_index = 0;
_version = list._version;
_current = default;
}
public void Dispose()
{
}
public bool MoveNext()
{
List<T> localList = _list;
if (_version == localList._version && ((uint)_index < (uint)localList._size))
{
_current = localList._items[_index];
_index++;
return true;
}
return MoveNextRare();
}
private bool MoveNextRare()
{
if (_version != _list._version)
{
ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
}
_index = _list._size + 1;
_current = default;
return false;
}
重點(diǎn)看上面的list,上面表面了,當(dāng)我們使用foreach 進(jìn)行遍歷的時(shí)候,如果我們進(jìn)行了刪除或者添加,那么_version就會(huì)發(fā)生變化,那么可想而知會(huì)拋出異常。
例子:
static void Main(string[] args)
{
List<object> lists = new List<object>();
lists.Add("123456");
lists.Add("1231246");
lists.Add("dsadadsads");
lists.Add("eqewqew");
foreach (var item in lists)
{
if (item.ToString() == "1231246")
{
lists.Remove(item);
}
}
Console.ReadLine();
}
然后就會(huì)拋出異常了。
那么這里就不介紹find了,find 就是遍歷數(shù)組,找出是否相等。
哦,對(duì)了講另外一個(gè)故事。
public int Count => _size;
count-1 就是當(dāng)前插入的位置。
那么如果你想刪除某個(gè)元素的時(shí)候,那么你可以進(jìn)行removeat 刪除,這樣避免了find。
那么非常值得注意的是如果刪除了其他元素,如果那么元素的位置小于你記錄的位置,那么應(yīng)該是位置進(jìn)行減一。
到此這篇關(guān)于c#系列 list詳情的文章就介紹到這了,更多相關(guān)c#系列 list內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#實(shí)現(xiàn)簡(jiǎn)易的計(jì)算器
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)簡(jiǎn)易的計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04
重寫(xiě)、隱藏基類(new, override)的方法
重寫(xiě)、隱藏基類(new, override)的方法,需要的朋友可以參考一下2013-03-03
C#12中的Collection expressions集合表達(dá)式語(yǔ)法糖詳解
C#12中引入了新的語(yǔ)法糖來(lái)創(chuàng)建常見(jiàn)的集合,并且可以使用..來(lái)解構(gòu)集合,將其內(nèi)聯(lián)到另一個(gè)集合中,下面就跟隨小編一起學(xué)習(xí)一下C#12中這些語(yǔ)法糖的使用吧2023-11-11
C#中使用WinRAR實(shí)現(xiàn)加密壓縮及解壓縮文件
這篇文章主要介紹了C#中使用WinRAR實(shí)現(xiàn)加密壓縮及解壓縮文件,本文直接給出實(shí)例代碼,代碼中包含詳細(xì)注釋,需要的朋友可以參考下2015-07-07
使用Linq注意事項(xiàng)避免報(bào)錯(cuò)的方法
這篇文章主要介紹了使用Linq注意事項(xiàng)避免報(bào)錯(cuò)的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
C#實(shí)現(xiàn)基于IE內(nèi)核的簡(jiǎn)單瀏覽器完整實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)基于IE內(nèi)核的簡(jiǎn)單瀏覽器,較為詳細(xì)的分析了C#實(shí)現(xiàn)瀏覽器的原理與主要功能實(shí)現(xiàn)方法,并附帶完整實(shí)例供大家下載,需要的朋友可以參考下2015-07-07
C#發(fā)送和接收HTTP請(qǐng)求類HttpWebRequest的用法
這篇文章主要給大家介紹了關(guān)于C#發(fā)送和接收HTTP請(qǐng)求類HttpWebRequest用法的相關(guān)資料,C#中的HttpWebRequest是一個(gè)用于發(fā)送HTTP請(qǐng)求的類,它可以用于向Web服務(wù)器發(fā)送GET、POST、PUT、DELETE等請(qǐng)求,需要的朋友可以參考下2024-06-06

