Skip to content

可变类型的 Map

小马哥 edited this page Dec 19, 2016 · 6 revisions

Hprose 除了提供了方便的可变类型的 List,还提供了非常好用的可变类型的 Map。可变类型的 Map 也是基于接口来实现的,因此也具有引用计数的自动内存管理功能。

继承关系图

 +----------------+   +-------------------+         +----------------+     
 | IReadWriteSync |   | TInterfacedObject |         | IMapEnumerator |     
 +----------------+   +-------------------+         +----------------+     
          |                     |                                          
          v                     v                                          
      +------+          +--------------+           +----------------------+
      | IMap |--------->| TAbstractMap |<----------| IInvokeableVarObject |
      +------+          +--------------+           +----------------------+
          |                     |                                          
          |                     |               +-------------------------+
 +--------+---------------------+-------------->| ICaseInsensitiveHashMap |
 |        |                     |               +-------------------------+
 |        |                     |                            |             
 |        v                     v                            v             
 |  +----------+          +----------+          +-------------------------+
 +--| IHashMap |--------->| THashMap |--------->| TCaseInsensitiveHashMap |
    +----------+          +----------+          +-------------------------+
          |                     |                                          
          |                     |             +---------------------------+
+---------+---------------------+------------>| ICaseInsensitiveHashedMap |
|         |                     |             +---------------------------+
|         |                     |                           |              
|         v                     v                           v              
|  +------------+        +------------+       +---------------------------+
+--| IHashedMap |------->| THashedMap |------>| TCaseInsensitiveHashedMap |
   +------------+        +------------+       +---------------------------+

IMap 接口

同 List 一样,Hprose 提供的所有 Map 类也都是通过接口来存取的,它们都实现了 IMap 接口。

IMap 接口中定义了枚举器操作,因此,在 Delphi 2005 以上或 FreePascal 2.5.1 以上版本中,您可以通过 for...in 语句来操作 IMap 接口的对象。枚举的元素类型为 TMapEntry,该类型是一个记录体,它有 2 个元素:KeyValue,这两个元素都是 Variant 类型的。

IMap 接口上定义了添加(PutPutAll),获取(Get),查找(ContainsKeyContainsValue),删除(Delete),清空(Clear),复制(Assign),同步(LockUnLock)等操作。

IMap 接口还定义了直接通过键值返回元素值的默认属性,可以让您向操作数组一样存取元素值,还定义了通过元素值获取键值的索引属性(Key)。另外,它还定义了元素个数(Count)、所有键值(Keys)和所有元素值(Values)这三个属性。

其定义如下:

  IMap = interface(IReadWriteSync)
  ['{28B78387-CB07-4C28-B642-09716DAA2170}']
    procedure Assign(const Source: IMap);
    function GetCount: Integer;
    function GetKeys: IImmutableList;
    function GetValues: IImmutableList;
    function GetKey(const AValue: Variant): Variant;
    function GetValue(const AKey: Variant): Variant;
    procedure PutValue(const AKey: Variant; AValue: Variant);
    function Get(const AKey: Variant): Variant; overload;
    function Get(const AKey: Variant; out AValue: Variant): Boolean; overload;
    procedure Put(const AKey, AValue: Variant); overload;
    procedure Put(const AList: IImmutableList); overload;
    procedure Put(const AMap: IMap); overload;
    procedure Put(const Container: Variant); overload;
    procedure Put(const ConstArray: array of const); overload;
    function Add(const AKey, AValue: Variant): Boolean;
    procedure Clear;
    function CompareKey(const Entry1, Entry2: TMapEntry): Integer;
    function CompareValue(const Entry1, Entry2: TMapEntry): Integer;
    function ContainsKey(const AKey: Variant): Boolean;
    function ContainsValue(const AValue: Variant): Boolean;
    function Delete(const AKey: Variant): Variant;
    function GetEnumerator: IMapEnumerator;
    function Join(const ItemGlue: string = ';';
                  const KeyValueGlue: string = '=';
                  const LeftPad: string = '';
                  const RightPad: string = ''): string;
    procedure InitLock;
    procedure InitReadWriteLock;
    procedure Lock;
    procedure Unlock;
    procedure PutAll(const AList: IImmutableList); overload;
    procedure PutAll(const AMap: IMap); overload;
    procedure PutAll(const Container: Variant); overload;
    procedure PutAll(const ConstArray: array of const); overload;
    function ToList(ListClass: TListClass; Sync: Boolean = True;
      ReadWriteSync: Boolean = False): IList;
    function ToArrayList(Sync: Boolean = True;
      ReadWriteSync: Boolean = False): IArrayList;
    procedure Sort; overload;
    procedure Sort(CompareProc: TMapCompareMethod); overload;
    procedure SortByValue;
    procedure TrimExcess;
    property Count: Integer read GetCount;
    property Key[const AValue: Variant]: Variant read GetKey;
    property Value[const AKey: Variant]: Variant read GetValue write PutValue; default;
    property Keys: IImmutableList read GetKeys;
    property Values: IImmutableList read GetValues;
  end;

后面我们会在介绍具体实现类的时候来详细讲解这些操作。

TAbstractMap 类

该类是所有可变类型 Map 的基类,它实现了 IMap 接口上的枚举器,复制和同步操作。它继承自 TInterfacedObject,因此它也继承了接口生存周期管理。它是一个抽象类,您不应该对它进行实例化。

如果您打算实现自己的可变类型 Map,那么您应该直接或间接的继承自它。因为在 Hprose 序列化和反序列化时,判断一个类是否是可变类型的 Map 是通过判断 TAbstractMap 是否是这个类的祖先类的方式完成的。

该类上还实现了一个类方法 Split。但因为 TAbstractMap 是抽象类,所以您只能在它的可实例化子类上调用它。该方法的具体用法,我们在下面介绍 THashMap 类时来详细介绍。

IHashMap 接口

  IHashMap = interface(IMap)
  ['{B66C3C4F-3FBB-41FF-B0FA-5E73D87CBE56}']
  end;

该接口继承自 IMap 接口,并且没有添加任何操作。你可能会觉得这样定义比较奇怪,因为在平时使用时,该接口似乎是没有什么用处的。

这样做的原因是为了在反序列化列表类型数据时,可以通过指定该具体的接口,来反序列化为具体的实现类对象。

IHashMap 接口对应 THashMap 实现。

IHashedMap 接口对应 THashedMap 实现。

ICaseInsensitiveHashMap 接口对应 TCaseInsensitiveHashMap 实现。

ICaseInsensitiveHashedMap 接口对应 TCaseInsensitiveHashedMap 实现。

如果反序列化时,指定的类型是 IMap 接口类型,那么反序列化时,也会反序列化为 THashMap 实现的具体对象。

关于序列化和反序列化的内容,我们会在后面具体的章节中在详细介绍。

THashMap 类

该类直接继承自 TAbstractMap,它是最常用的 Map。与其它语言中的 HashMap 不同,它的 Hash 存取是基于 THashedList 实现的,因此它不但可以高速存取数据,还可以保持数据插入的顺序。

创建 THashMap 对象

THashMap 有多个构造方法,其中一部分继承自 TAbstractMap

    constructor Create(ACapacity: Integer = 16; Factor: Single = 0.75; Sync: Boolean = True; ReadWriteSync: Boolean = False); overload; override;
    constructor Create(ACapacity: Integer; Sync: Boolean; ReadWriteSync: Boolean = False); overload; virtual;
    constructor Create(Sync: Boolean; ReadWriteSync: Boolean = False); overload; virtual;
    constructor Create(const AList: IImmutableList; Sync: Boolean = True; ReadWriteSync: Boolean = False); overload; virtual;
    constructor Create(const AMap: IMap; Sync: Boolean = True; ReadWriteSync: Boolean = False); overload; virtual;
    constructor Create(const Container: Variant; Sync: Boolean = True; ReadWriteSync: Boolean = False); overload; virtual;
    constructor Create(const ConstArray: array of const; Sync: Boolean = True; ReadWriteSync: Boolean = False); overload; virtual;

构造方法中的 ACapacity 参数表示初始化容量,如果您事先确知要放入的元素个数,那么将容量设置为与元素个数相同或多于元素个数,可以避免后面放入元素时内存的重新分配。默认初始化容量为 16。

初始化容量不代表这个 Map 只能放多少个元素,也不代表这个 Map 有多少个元素。它只代表在不重新分配内存的情况下,能放入的元素个数。当元素个数增长到超过这个容量时,这个容量会自动扩大,您不需要做任何特殊操作。

另一个参数是负载因子 Factor,其默认值为 0.75,这是时间和空间成本上一种折衷:减小负载因子可以减少 Hash 表所占用的内存空间,但会增加查询数据的时间开销;增加负载因子会提高数据查询的性能,但会增加 Hash 表所占用的内存空间。所以,通常不需要改变这个默认值。