下面的TCycleTable类已经可以实现在有限大小的空间内进行数据的循环存放(请注意—
—索引不是循环的——它只是自动重排而已)。
type
TDataRec=record
ID:Integer;
Day:TDate;
Money:Integer;
end;
TIndexRec=record
Offset:Integer;
StartNo,EndNo:Integer;
end;
TCycleTable=class
private
FData:array of TDataRec;
FIndex:array of TIndexRec;
FMaxIndexCount,FMaxRecCount:Integer;
FIndexHead,FIndexTail:Integer;
function GetRecTail:Integer;
procedure ResetIndex;
procedure ClearHeadRec;
function GetRecHead: Integer;
function GetData(Index: Integer): TDataRec;
function GetDataCount: Integer;
function GetIndex(Index: Integer): TIndexRec;
function GetIndexCount: Integer;
public
property Data[Index:Integer]:TDataRec read GetData;
default;
property DataCount:Integer read GetDataCount;
property Index[Index:Integer]:TIndexRec read GetIndex;
property IndexCount:Integer read GetIndexCount;
function AddRec(AData:TDataRec):Boolean;
constructor Create(MaxRecCount,MaxIndexCount:Integer);
destructor Destroy;
override;
end;
{ TCycleTable }
function TCycleTable.AddRec(AData: TDataRec): Boolean;
var
SavePos:Integer;
begin
Result:=false;
if FIndexHead=-1 then
//first node
begin
FIndexHead:=0;
FIndexTail:=0;
SavePos:=0;
FIndex[FIndexTail].Offset:=0;
FIndex[FIndexTail].StartNo:=AData.ID;
end
else
begin
if GetRecTail<FMaxRecCount-1 then
//记录没有到末尾
begin
SavePos:=GetRecTail+1;
if SavePos=FIndex[FIndexHead].Offset then
//发生了覆盖
ClearHeadRec;
if AData.ID<>FIndex[FIndexTail].EndNo+1 then
//不是连续追加的记录
begin
if FIndexTail-FIndexHead=FMaxIndexCount-1 then
//索引已用罄
exit;
if FIndexTail=FMaxIndexCount-1 then
//FIndex数组的后面已经没有空位
ResetIndex;
FIndexTail:=FIndexTail+1;
//在末尾新增索引
FIndex[FIndexTail].Offset:=SavePos;
FIndex[FIndexTail].StartNo:=AData.ID;
end;
end
else
begin
SavePos:=0;
ClearHeadRec;
//因为肯定发生了覆盖...
if FIndexTail-FIndexHead=FMaxIndexCount-1 then
//...
exit;
if FIndexTail=FMaxIndexCount-1 then
//...
ResetIndex;
FIndexTail:=FIndexTail+1;
//...
FIndex[FIndexTail].Offset:=SavePos;
FIndex[FIndexTail].StartNo:=AData.ID;
end;
end;
FIndex[FIndexTail].EndNo:=AData.ID;
with FData[SavePos]do
begin
ID:=AData.ID;
Day:=AData.Day;
Money:=AData.Money;
end;
Result:=true;
end;
procedure TCycleTable.ClearHeadRec;
begin
with FIndex[FIndexHead]do
if StartNo=EndNo then
//只对应了一条记录
Inc(FIndexHead)
else
begin
//后移
Inc(Offset);
Inc(StartNo);
end;
end;
constructor TCycleTable.Create(MaxRecCount,MaxIndexCount: Integer);
begin
if MaxRecCount>0 then
FMaxRecCount:=MaxRecCount
else
FMaxRecCount:=1;
if MaxIndexCount>0 then
FMaxIndexCount:=MaxIndexCount
else
FMaxIndexCount:=1;
SetLength(FData,FMaxRecCount);
SetLength(FIndex,FMaxIndexCount);
FIndexHead:=-1;
FIndexTail:=-1;
end;
destructor TCycleTable.Destroy;
begin
inherited;
SetLength(FData,0);
SetLength(FIndex,0);
end;
function TCycleTable.GetData(Index: Integer): TDataRec;
begin
Result:=FData[(Index+GetRecHead+FMaxRecCount) mod FMaxRecCount];
end;
function TCycleTable.GetDataCount: Integer;
begin
if GetRecHead>=0 then
Result:=1+(GetRecTail-GetRecHead+FMaxRecCount) mod FMaxRecCount
else
Result:=0;
end;
function TCycleTable.GetIndex(Index: Integer): TIndexRec;
begin
Result:=FIndex[(Index+FIndexHead+FMaxIndexCount) mod FMaxIndexCount];
end;
function TCycleTable.GetIndexCount: Integer;
begin
if FIndexHead>=0 then
Result:=FIndexTail-FIndexHead+1
else
Result:=0;
end;
function TCycleTable.GetRecHead:Integer;
begin
if FIndexHead<0 then
Result:=-1
else
Result:=FIndex[FIndexHead].Offset;
end;
function TCycleTable.GetRecTail: Integer;
begin
if FIndexTail<0 then
Result:=-1
else
with FIndex[FIndexTail]do
Result:=Offset+EndNo-StartNo;
end;
procedure TCycleTable.ResetIndex;
//重排索引数组
var
i:Integer;
begin
for i:=FIndexHead to FIndexTaildo
Move(FIndex,FIndex[i-FIndexHead],SizeOf(TIndexRec));
FIndexTail:=FIndexTail-FIndexHead;
FIndexHead:=0;
end;
使用范例:
var
CycleTb:TCycleTable;
procedure TForm1.FormCreate(Sender: TObject);
begin
CycleTb:=TCycleTable.Create(10,5);
//最多10条记录,5个索引
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
CycleTb.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Data:TDataRec;
i:Integer;
begin
Data.Day:=Date+Random(100);
Data.ID:=SpinEdit1.Value;
Data.Money:=Random(10000);
CycleTb.AddRec(Data);
with Memo1do
begin
Text:='';
for i:=0 to CycleTb.DataCount-1do
Lines.Add(Format('[%4d] %s %6d',[CycleTb.ID,DateToStr(CycleTb.Day),CycleTb.Money]));
end;
with Memo2do
begin
Text:='';
for i:=0 to CycleTb.IndexCount-1do
Lines.Add(Format('%4d: %d - %d',[
CycleTb.Index.Offset,CycleTb.Index.StartNo,CycleTb.Index.EndNo]));
end;
SpinEdit1.Value:=SpinEdit1.Value+1;
end;
至于查询,只需要遍历CycleTb,挨个比较就可以了,在此就不示范了。
运行结果:
Index:
2: 8 - 8
3: 12 - 16
8: 21 - 22
0: 23 - 24
Data:
[ 8] 2003-10-8 6716
[ 12] 2003-10-12 1617
[ 13] 2003-10-18 4256
[ 14] 2003-9-19 4747
[ 15] 2003-9-18 8408
[ 16] 2003-9-16 2932
[ 21] 2003-12-11 3679
[ 22] 2003-11-27 3279
[ 23] 2003-11-19 8441
[ 24] 2003-11-21 3066
但愿没有误解您的意思...