對普通鏈表進行添加和刪除操作,會創建和銷燬對象,如果操作太頻繁會對GC造成壓力,而遊標鏈表是事先分配好一個大數組,然後用數組的下標代替普通鏈表的引用指針,這樣鏈表節點的添加刪除,只是下標指向的改變,不會創建和銷燬對象,相當於自己管理了內存,所以降低了GC的壓力。
性能測試模型:
1、往鏈表裏添加500w個int節點
2、把這500w個節點刪除
3、重複1和2進行10次,分別記錄各代gc回收次數,gc堆大小及執行時間。
性能測試結果
遊標鏈表
err:0
gen:24,gen:15,gen:5
totalMemory:160217492
tack time:19358
.NET自帶普通鏈表
gen:213,gen:125,gen:21
totalMemory:120221380
tack time:18024
性能測試結論:
對遊標鏈表的頻繁添加、刪除操作引起的GC回收次數明顯比普通鏈表要少的多,但遊標鏈表因爲使用了額外的數組記錄空閒節點列表,所以佔用內存大一些,由於每個標準操作的子步驟數比普通鏈表多一些,所以總體銷號時間也稍大於普通鏈表,但不明顯。
單元測試用例:
操作:
1、往鏈表的頭上依次添加節點1,2,3加上默認的0節點共4個節點。
2、從鏈表的尾部連續刪除兩個節點
3、從鏈表的尾部開始,向前遍歷節點,並打印節點數據
預期
輸出數據2,3
單元測試結果:
通過
測試部分代碼
class Program {
static void Main(string[] args) {
//UnitTest();
const int max = 500*10000;
Stopwatch watch = Stopwatch.StartNew();
//TestLinkdList(max, 10); //這一句和下一句要分別測試,別同時測
TestCursorList(max, 10);
Console.WriteLine("gen:{0},gen:{1},gen:{2}",
GC.CollectionCount(0),GC.CollectionCount(1),GC.CollectionCount(2));
Console.WriteLine("totalMemory:{0}",GC.GetTotalMemory(false));
Console.WriteLine("tack time:{0}",watch.ElapsedMilliseconds);
Console.ReadKey();
}
private static void UnitTest() {
CursorList<int> list = new CursorList<int>(3);
list.AddHeader(1);
list.AddHeader(2);
list.AddHeader(3);
list.RemoveTail();
list.RemoveTail();
CursorListNode<int> node = list.Tail;
while (node != null) //預期輸出2,3
{
Console.WriteLine(node.Data);
node = node.Next;
}
}
private static void TestCursorList(int max, int iteration)
{
CursorList<int> list = new CursorList<int>(max);
int err = 0;
for (int k = 0; k < iteration; k++) {
for (int i = 0; i < max; i++)
if (list.AddHeader(i) == null) err++;
for (int i = 0; i < max; i++)
list.RemoveTail();
}
Console.WriteLine("err:{0}", err);
}
private static void TestLinkdList(int max, int iteration) {
LinkedList<int> list = new LinkedList<int>();
for (int k = 0; k < iteration; k++) {
for (int i = 0; i < max; i++)
list.AddFirst(i);
for (int i = 0; i < max; i++)
list.RemoveLast();
}
}
}
static void Main(string[] args) {
//UnitTest();
const int max = 500*10000;
Stopwatch watch = Stopwatch.StartNew();
//TestLinkdList(max, 10); //這一句和下一句要分別測試,別同時測
TestCursorList(max, 10);
Console.WriteLine("gen:{0},gen:{1},gen:{2}",
GC.CollectionCount(0),GC.CollectionCount(1),GC.CollectionCount(2));
Console.WriteLine("totalMemory:{0}",GC.GetTotalMemory(false));
Console.WriteLine("tack time:{0}",watch.ElapsedMilliseconds);
Console.ReadKey();
}
private static void UnitTest() {
CursorList<int> list = new CursorList<int>(3);
list.AddHeader(1);
list.AddHeader(2);
list.AddHeader(3);
list.RemoveTail();
list.RemoveTail();
CursorListNode<int> node = list.Tail;
while (node != null) //預期輸出2,3
{
Console.WriteLine(node.Data);
node = node.Next;
}
}
private static void TestCursorList(int max, int iteration)
{
CursorList<int> list = new CursorList<int>(max);
int err = 0;
for (int k = 0; k < iteration; k++) {
for (int i = 0; i < max; i++)
if (list.AddHeader(i) == null) err++;
for (int i = 0; i < max; i++)
list.RemoveTail();
}
Console.WriteLine("err:{0}", err);
}
private static void TestLinkdList(int max, int iteration) {
LinkedList<int> list = new LinkedList<int>();
for (int k = 0; k < iteration; k++) {
for (int i = 0; i < max; i++)
list.AddFirst(i);
for (int i = 0; i < max; i++)
list.RemoveLast();
}
}
}
遊標鏈表實現代碼
爲了簡單起見,只是先了在頭部添加和移除尾部節點的方法。
public class CursorListNode<T> {
public CursorListNode<T> Next { get; set; }
public CursorListNode<T> Prior { get; set; }
internal int Index { get; set; }
public T Data { get; set; }
}
public class CursorList<T> {
private readonly Queue<int> _freeQ = null;
private readonly CursorListNode<T>[] _list = null;
public CursorList(int capacity) {
_freeQ = new Queue<int>(capacity+1);
_list = new CursorListNode<T>[capacity+1];
_list[0] = Header = Tail = new CursorListNode<T> {
Index = 0,
Data = default(T),
Next = null,
Prior = null
};
for (int i = 1; i < capacity+1; i++)
{
_freeQ.Enqueue(i);
_list[i] = new CursorListNode<T> {
Index = -1,
Data = default(T),
Next = null,
Prior = null
};
}
}
public CursorListNode<T> AddHeader(T data) {
if(_freeQ.Count < 1) return null;
int newIndex = _freeQ.Dequeue();
CursorListNode<T> newNode = _list[newIndex];
newNode.Index = newIndex;
newNode.Data = data;
newNode.Next = null;
newNode.Prior = Header;
Header.Next = newNode;
Header = newNode;
return newNode;
}
public void RemoveTail() {
if(Tail == null) return;
_freeQ.Enqueue(Tail.Index);
if (Tail.Next != null)
{
Tail = _list[Tail.Next.Index];
Tail.Prior = null;
}
else
Tail = null;
}
public CursorListNode<T> Header { get; set; }
public CursorListNode<T> Tail { get; set; }
}
public CursorListNode<T> Next { get; set; }
public CursorListNode<T> Prior { get; set; }
internal int Index { get; set; }
public T Data { get; set; }
}
public class CursorList<T> {
private readonly Queue<int> _freeQ = null;
private readonly CursorListNode<T>[] _list = null;
public CursorList(int capacity) {
_freeQ = new Queue<int>(capacity+1);
_list = new CursorListNode<T>[capacity+1];
_list[0] = Header = Tail = new CursorListNode<T> {
Index = 0,
Data = default(T),
Next = null,
Prior = null
};
for (int i = 1; i < capacity+1; i++)
{
_freeQ.Enqueue(i);
_list[i] = new CursorListNode<T> {
Index = -1,
Data = default(T),
Next = null,
Prior = null
};
}
}
public CursorListNode<T> AddHeader(T data) {
if(_freeQ.Count < 1) return null;
int newIndex = _freeQ.Dequeue();
CursorListNode<T> newNode = _list[newIndex];
newNode.Index = newIndex;
newNode.Data = data;
newNode.Next = null;
newNode.Prior = Header;
Header.Next = newNode;
Header = newNode;
return newNode;
}
public void RemoveTail() {
if(Tail == null) return;
_freeQ.Enqueue(Tail.Index);
if (Tail.Next != null)
{
Tail = _list[Tail.Next.Index];
Tail.Prior = null;
}
else
Tail = null;
}
public CursorListNode<T> Header { get; set; }
public CursorListNode<T> Tail { get; set; }
}
參考鏈接:
CursorList.cpp - Implementation for cursor linked list
http://www.cplusplus.happycodings.com/Data-Structures-and-Algorithm-Analysis-in-C++/code33.html
鏈表的遊標法實現 cursor_list
http://blog.csdn.net/liuzongqiang/archive/2008/01/06/2027762.aspx