2023-04-03 11:04:31 +08:00

254 lines
6.6 KiB
C#

using System.Collections;
using System.Collections.Generic;
namespace BF
{
//双向循环列表
public class BFLinkedList<T> : IEnumerable<T>
{
public T FirstItem { get { return First == null ? default(T) : First.item; } }
public T LastItem { get { return Last == null ? default(T) : Last.item; } }
public BFNode<T> First { get { return head; } }
public BFNode<T> Last { get { return head == null ? null : head.prev; } }
private BFNode<T> head;
private int count;
public int Count { get { return count; } }
private int version;
public int Version { get { return version; } }
private BFStackPool<T> nodePool;
private IEqualityComparer<T> comparer;
public BFLinkedList(IEqualityComparer<T> comp = null)
{
nodePool = new BFStackPool<T>();
comparer = comp ?? EqualityComparer<T>.Default;
}
public BFNode<T> AddFirst(T t)
{
var newNode = nodePool.SafePop(); //Alloc();
newNode.item = t;
if (null == head)
{
newNode.prev = newNode.next = newNode;
head = newNode;
++count;
++version;
}
else
{
InternalInsertNodeBefore(head, newNode);
head = newNode;
}
return null;
}
public BFNode<T> AddLast(T t)
{
var newNode = nodePool.SafePop();//Alloc();
newNode.item = t;
if (count == 0)
{
newNode.prev = newNode.next = newNode;
head = newNode;
++count;
++version;
}
else
InternalInsertNodeBefore(head, newNode);
return newNode;
}
public void AddBefore(BFNode<T> node, T t)
{
var newNode = nodePool.SafePop();//Alloc();
newNode.item = t;
InternalInsertNodeBefore(node, newNode);
if (node == head)
head = newNode;
}
public void AddAfter(BFNode<T> node, T t)
{
var newNode = nodePool.SafePop();//Alloc();
newNode.item = t;
if (null == node.next)
{
node.next = newNode;
newNode.prev = node;
}
else
{
InternalInsertNodeBefore(node.next, newNode);
}
}
public bool Contains(T value)
{
return null != Find(value);
}
public BFNode<T> Find(T value)
{
var node = head;
if (null == node)
return null;
do
{
if (comparer.Equals(node.item, value))
return node;
node = node.next;
}
while (node != head);
return null;
}
public BFNode<T> FindLast(T value)
{
if (null == head)
return null;
var last = head.prev;
var node = last;
do
{
if (comparer.Equals(node.item, value))
return node;
node = node.prev;
}
while (last != node);
return null;
}
public void Remove(T value)
{
var node = Find(value);
if (node == null)
return;
InternalRemoveNode(node);
}
public void Clear()
{
var current = head;
while (current != null)
{
var temp = current;
current = current.next;
temp.Invalidate();
nodePool.Push(temp);
}
}
private void InternalRemoveNode(BFNode<T> node)
{
if (node.next == node)
{
head = null;
}
else
{
node.next.prev = node.prev;
node.prev.next = node.next;
if (head == node)
head = node.next;
}
node.Invalidate();
nodePool.Push(node);
--count;
++version;
}
private void InternalInsertNodeBefore(BFNode<T> node, BFNode<T> newNode)
{
newNode.next = node;
newNode.prev = node.prev;
node.prev.next = newNode;
node.prev = newNode;
++count;
++version;
}
public IEnumerator GetEnumerator()
{
return new Enumerator(this);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return new Enumerator(this);
}
public class Enumerator : IEnumerator<T>
{
#region interface
public T Current { get { return current; } }
object System.Collections.IEnumerator.Current { get { return current; } }
public bool MoveNext()
{
CheckInvalidOperation();
if (node == null)
{
index = linkedList.Count + 1;
return false;
}
++index;
current = node.item;
node = node.next;
if (node == linkedList.head)
node = null;
return true;
}
public void Reset()
{
CheckInvalidOperation();
current = default(T);
node = linkedList.head;
version = linkedList.Version;
index = 0;
}
public void Dispose()
{
linkedList = null;
current = default(T);
node = null;
}
#endregion
private T current;
private BFNode<T> node;
private int version;
private int index;
public BFLinkedList<T> linkedList;
public Enumerator(BFLinkedList<T> ll)
{
linkedList = ll;
node = ll.head;
version = ll.Version;
current = default(T);
index = 0;
}
private void CheckInvalidOperation()
{
if (version != linkedList.Version)
throw new System.InvalidOperationException("foreach 不能修改结构");
}
}
}
}