52704.fb2
hsKey := '';
{$ENDIF}
end;
except
{при возникновении исключения попытаться очистить хеш-таблицу и оставить ее в непротиворечивом состоянии}
FTable.Free;
FTable :=0ldTable;
raise;
end;
{и, наконец, освободить старую таблицу}
OldTable.Free;
end;
procedure TtdHashTableLinear.htlGrowTable;
begin
{увеличить размер таблицы приблизительно в два раза по сравнению с предыдущим}
htlAlterTableSize(GetClosestPrime(suce(FTable.Count * 2)));
end;
Метод hltAlterTableSize содержит код выполнения этих операций. Он работает, сохраняя текущую хеш-таблицу (т.е. экземпляр списка записей), распределяя память под новую таблицу и, затем, просматривая все элементы в старой таблице (которые находятся в ячейках, помеченных как "используемые") и вставляя их в новую таблицу. В заключение, метод освобождает старую таблицу. Обратите внимание, что блок Try..except предпринимает попытку сохранить непротиворечивое состояние хеш-таблицы в случае возникновения исключения. Естественно, при этом предполагается, что в момент вызова метода хеш-таблица находилась в именно таком состоянии.
Излишне говорить, что расширение хеш-таблицы - довольно-таки трудоемкая операция (которая требует очень большого дополнительного объема свободной памяти - вдвое больше того, который уже был выделен). Всегда желательно приблизительно оценить общее количество строк, которые нужно вставить В хеш-таблицу, и добавить, скажем, еще половину этого количества строк. Результирующее значение можно использовать в качестве расчетного размера хеш-таблицы при ее создании. Такая оценка обеспечит нам определенную свободу действий при использовании хеш-таблицы.
Теперь пора разобраться с последним фрагментом головоломки: рассмотреть "закулисный" метод htlIndexOf - примитив, используемый методами Insert, Delete и Find.
Листинг 7.10. Примитив поиска ключа в хеш-таблице
function TtdHashTableLinear.htlIndexOf(const aKey : string; var aSlot : pointer): integer;
var
Inx : integer;
CurSlot : PHashSlot;
FirstInx : integer;
begin
{вычислить хеш-значение строки, запомнить его, чтобы можно было установить, когда будет (если вообще будет) выполнен просмотр всех записей таблицы}
Inx := FHashFunc(aKey, FTable.Count);
FirstInx := Inx;
{выполнить без каких-либо ограничений — при необходимости, выход из цикла можно будет осуществить всегда}
while true do
begin {для текущей ячейки}
CurSlot := PHashSlot(FTable[Inx]);
with CurSlot^ do
begin
if not hsInUse then begin
{ ячейка "пуста "; необходимо прекратить линейное зондирование и вернуть эту ячейку}
aSlot := CurSlot;
Result := -1;
Exit;
end
else begin
{ ячейка "используется"; необходимо проверить, совпадает ли она с искомым ключом. Если да, то необходимо осуществить выход, возвращая индекс и ячейку}
{$IFDEF Delphi1}
if (hsKey^ = aKey) then begin
{$ELSE}
if (hsKey = aKey) then begin
{$ENDIF}