zoukankan      html  css  js  c++  java
  • Delphi中Hash表实现思路

    Hash表,其实就是一个Key对应一个Object
    在Delphi里最简单实现Hash的就是TStrings
    通过它的AddObject,IndexOf,Objects等方法可以做一个很简单的Hash表。
    TStrings没有排序,所以IndexOf比较慢,而它的子类TStringList具有Stored属性,设置为True之后,IndexOf是用折半查找的,效率很高。
    所以在Delphi里可以用TstringList做一个简单的Hash。

    但是TStringList做Hash是有一些限制性的
    1.只限字符串做Key了
    2.HashCode冲突没法解决
    3.不可并发

    类似TStrings及其子类的还有TList及类似List等。
    但是这些都存在着Hash冲突和并发问题。

    现在就来在Delphi的基础的TList和TInterfaceList的基础上做一个 通用的可并发并且有效解决Hash冲突的Hash表。

    先是总体结构
    Hash表是一个List,就用TList就行了,Hash表类THashTable
    为了解决Hash冲突,每个Hash的List的一个点其实也应该是一个对列,我这里采用了TinterfaceList,冲突列表类THashItemQueue,原因后面再讲。
    这样做出来其实就像一个二维数组一样,一维的是Hash队列THashTable,二维的HashCode冲突的队列THashItemQueue。
    THashTable 1:n THashItemQueue

    再来考虑并发问题
    以THashTable 的List来说,它是通过HashCode定位的,并且容量大小最好先固定下来,这样Hash表本身就不存在并发问题,本身就可支持并发了。
    但是Hash冲突列表THashItemQueue因为在查找添加删除时都会有并发问题,所以,为每一个THashItemQueue实例做临界区保护即可。

    再就是通用性考虑。
    为了适应不同的HashCode计算,和节点比较
    所以应该把这两个接口外放,即要由调用者实现,而ThashTable只提供一个容器和接口
    先看HashCode,HashCode计算其实就是一个函数,通过计算节点的Key值,计算出在Hash表中的位置。所以可以定义一个函数引用。TCalHashCode = function(Key: Pointer): Integer of object; 而THashTable 里设置一个TCalHashCode 属性,调用者实现这个函数并传给THashTable
    而节点与Key之间的比较,可以通过让节点实现ComparetoKey接口的
    //--比较的接口
    ICompare = interface
    ['{C43DAF4B-E08D-4E09-B830-342D1B10B7B7}'] //一定要GUID,以做接口转换
    // a>b 返回 1,相等=0, a<b返回-1
    //为了适应数值类型和对象引用,故采用指针方式传参
    function CompareToKey(Key: Pointer): Integer;
    end;
    这样就可以把Key的比较完全外放,而达到ThashTable完全通用的目的。

    再来考虑另一个重要的问题:节点的生命周期问题。
    节点何时应该被正确释放?
    我采用的方法是让接口来管理节点生命周期的,当没有对一个节点对象的任何引用时,该节点被释放。

    综上,以下是该Hash的定义和实现代码
    {
    Hash 表及相关定义和实现
    晚起的小虫
    2006-6-11
    THashTable 为Hash表
    TCalHashCode 为计算Hash值的函数。外部传入到THashTable
    THashItemQueue 为解决Hash值冲突的链表
    ICompare 比较的接口,在Hash表中查找Key时用,节点对象必须实现该接口。
    TItemObject 节点虚类。
    }
    unit uHash;

    interface
    uses
    Classes, SysUtils, Forms, SyncObjs ,Windows;
    type
    //----------------节点----------------------
    //--比较的接口
    ICompare = interface
    ['{C43DAF4B-E08D-4E09-B830-342D1B10B7B7}'] //一定要GUID,以做接口转换
    // a>b 返回 1,相等=0, a<b返回-1
    //为了适应数值类型和对象引用,故采用指针方式传参
    function CompareToKey(Key: Pointer): Integer;
    end;

    //--节点纯虚类
    TItemObject = class(TInterfacedObject, ICompare)
    public
    function CompareToKey(Key: Pointer): Integer; virtual; abstract;
    end;
    //---------------节点链表----------------------------
    //--Hash节点链表,解决Hash冲突。
    {
    THashItemQueue以保存接口的方式保存对象引用,要保存的节点对象必须实现IInterface和ICompare接口
    }
    THashItemQueue = class
    protected
    FLock: TCriticalSection;
    FList: TInterfaceList;
    function GetItems(i: Integer): IInterface;
    function GetCount: Integer;
    function SordFind(Key: Pointer;var Index:Integer):Boolean;
    public
    constructor Create;
    destructor Destroy; override;
    property Items[i: Integer]: IInterface read GetItems;
    //按Key查找,找到返回节点的位置,找不到返回-1
    function IndexOf(Key: Pointer): Integer;
    //按Key查找,找到返回节点接口,找不到返回nil
    function Find(Key: Pointer): IInterface;

    property Count: Integer read GetCount;
    //添加节点,重复的忽略
    function Add(Key: Pointer; Item: IInterface):Boolean;
    procedure Delete(i: Integer);
    procedure Lock; //进入临界区锁定
    procedure UnLock; //解除临界区锁定
    end;

    //----------------HashTable---------------------------
    //--利用接口的引用计数来统计当前正在被Lock中的THashItemQueue个数统计
    TLockStater = class(TInterfacedObject)
    protected
    function GetLockCount: Integer;
    public
    property LockCount: Integer read GetLockCount;
    end;

    //--计算Hash的函数
    TCalHashCode = function(Key: Pointer): Integer of object;

    //--Hash接口
    IHashTable = interface
    ['{A05C330A-DA86-4909-BBF5-B98931293735}']
    function Find(Key: Pointer): IInterface;
    procedure Add(Key: Pointer; obj: IInterface);
    procedure Delete(Key: Pointer);
    end;

    //--Hash链表
    THashTable = class(TInterfacedObject, IHashTable)
    private
    FItemsCount: Integer;
    function GetUseItemCount: Integer;
    protected
    FLockStaterObj: TLockStater;
    FLockStater: IInterface; //Lock计数自引用接口
    FCount: Integer;
    FCalHashCode: TCalHashCode;
    FList: TList;
    function FindItem(Key: Pointer): THashItemQueue;
    procedure Error(Msg:string);
    public
    constructor Create(ItemsCount: Integer; CalHashCode: TCalHashCode);
    destructor Destroy; override;

    //查找,找到返回对象接口,找不到返回nil
    function Find(Key: Pointer): IInterface;
    //添加接口,如果Key已存在,则不添加
    procedure Add(Key: Pointer; obj: IInterface);
    //删除一个Key
    procedure Delete(Key: Pointer);
    property Count: Integer read FCount;
    property ItemsCount:Integer read FItemsCount;
    property UseItemCount: Integer read GetUseItemCount;
    end;
    //----------------------------------------------
    implementation

    { TLockStater }

    function TLockStater.GetLockCount: Integer;
    begin
    Result := FRefCount;
    end;

    { THashItemQueue }

    function THashItemQueue.Add(Key: Pointer; Item: IInterface):Boolean;
    var
    i :integer;
    begin
    if SordFind(Key,i) then
    begin
    Result := False;
    Exit;//如果已存在则退出
    end;
    FList.Insert(i,Item);
    Result := True;
    end;

    constructor THashItemQueue.Create;
    begin
    inherited;
    FList := TInterfaceList.Create;
    FLock := TCriticalSection.Create;
    end;

    procedure THashItemQueue.Delete(i: Integer);
    begin
    FList.Delete(i);
    end;

    destructor THashItemQueue.Destroy;
    var
    i: Integer;
    begin
    for i := FList.Count - 1 downto 0 do
    begin
    FList.Delete(i);
    end;

    FLock.Free;
    FList.Free;
    inherited;
    end;

    function THashItemQueue.Find(Key: Pointer): IInterface;
    var
    i: Integer;
    begin
    i := indexOf(Key);
    if i >= 0 then Result := FList.Items[i]
    else Result := nil;
    end;

    function THashItemQueue.GetCount: Integer;
    begin
    Result := FList.Count;
    end;

    function THashItemQueue.GetItems(i: Integer): IInterface;
    begin
    Result := FList.Items[i];
    end;

    function THashItemQueue.IndexOf(Key: Pointer): Integer;
    var
    i: Integer;
    begin
    Result := -1;
    if SordFind(Key,i) then Result := i;
    end;

    procedure THashItemQueue.Lock;
    begin
    FLock.Enter;
    end;

    function THashItemQueue.SordFind(Key: Pointer; var Index: Integer): Boolean;
    var
    L, H, I, C: Integer;
    begin
    //折半查找
    Result := False;
    L := 0;
    H := Count - 1;
    while L <= H do
    begin
    I := (L + H) shr 1;
    C := (FList.Items[i] as ICompare).CompareToKey(Key);
    if C < 0 then L := I + 1 else
    begin
    H := I - 1;
    if C = 0 then
    begin
    Result := True;
    L := I;
    end;
    end;
    end;
    Index := L;
    end;

    procedure THashItemQueue.UnLock;
    begin
    FLock.Leave;
    end;

    { THashTable }

    procedure THashTable.Add(Key: Pointer; obj: IInterface);
    var
    Item: THashItemQueue;
    tmp: IInterface;
    begin
    tmp := FLockStater;
    Item := FindItem(Key);
    Item.Lock;
    try
    if Item.Add(Key, obj) then InterlockedIncrement(FItemsCount)
    else
    Error('Key已存在!添加失败!');
    finally
    Item.UnLock;
    end;
    end;

    constructor THashTable.Create(ItemsCount: Integer; CalHashCode: TCalHashCode);
    var
    i: Integer;
    begin
    inherited Create;
    FCount := ItemsCount;
    FCalHashCode := CalHashCode;
    FList := TList.Create;
    for i := 0 to FCount - 1 do
    begin
    FList.Add(THashItemQueue.Create);
    end;
    FLockStaterObj := TLockStater.Create;
    FLockStater := FLockStaterObj;
    end;

    procedure THashTable.Delete(Key: Pointer);
    var
    Item: THashItemQueue;
    i: Integer;
    tmp: IInterface;
    begin
    tmp := FLockStater;
    Item := FindItem(Key);
    Item.Lock;
    try
    i := Item.IndexOf(Key);
    if i >= 0 then
    begin
    Item.Delete(i);
    InterlockedDecrement(FItemsCount);
    end
    else
    Error('要删除的Key不存在!');
    finally
    Item.UnLock;
    end;
    end;

    destructor THashTable.Destroy;
    var
    i: Integer;
    begin
    for i := FCount - 1 downto 0 do
    TObject(FList.Items[i]).Free;

    FList.Free;
    FLockStater := nil;
    inherited;
    end;

    procedure THashTable.Error(Msg: string);
    begin
    raise Exception.Create(Msg);
    end;

    function THashTable.Find(Key: Pointer): IInterface;
    var
    Item: THashItemQueue;
    i: Integer;
    tmp: IInterface;
    begin
    tmp := FLockStater;
    Item := FindItem(Key);
    Item.Lock;
    try
    Result := Item.Find(Key);
    finally
    Item.UnLock;
    end;
    end;

    function THashTable.FindItem(Key: Pointer): THashItemQueue;
    var
    i: integer;
    begin
    i := FCalHashCode(Key);
    Result := THashItemQueue(FList.Items[i]);
    end;

    function THashTable.GetUseItemCount: Integer;
    begin
    Result := FLockStaterObj.LockCount;
    end;
    end.

  • 相关阅读:
    Selenium 验证
    Flask 拓展(flask-admin)
    读取 xlsx中数据
    OSS 上传内容
    Tornado 端口绑定方式
    Tornado 基础
    Flask 懒人版分页(未完善)
    kafka事务原理、事务API和使用场景(转)
    jvm几种垃圾回收机制小结
    理解Semaphore及其用法详解(转)  -实现一个文件允许的并发访问数
  • 原文地址:https://www.cnblogs.com/huangjacky/p/1845742.html
Copyright © 2011-2022 走看看