Хеш таблица (словарь)
Это что-то похожее на хеш таблицу. На самом деле , в реальной хеш таблице хешируется и ячейка где располагаются данные и ключ. Так же в хеш таблице учитывается безопасность , путем например случайной функции хеширования. Так же в основном хеш таблицы хранят только указатели на данные. А сами данные (их размещение и очистка) контролируются отдельными механизмами. Хеш функция выполняет только механизм индексации.
В моем варианте:
1) хешируется только ячейка , где содержатся данные
для более быстрого доступа.
2) ключ хранится в хеш таблице как он
есть и идет строковое сравнение ключей при доступе. Это конечно увеличивает
время доступа к ячейке , но надежно.
3) данные так же хранятся в таблице ,
хотя есть возможность хранить только указатели.
4) функция
хеширования на данный момент не меняется и не думаю , что это мне
нужно. Ведь я создавал эту релизацию не для серверов и не для защиты информации.
Мне нужен был просто более быстрый механизм хранения данных , чем например
дерево или связанный список.
5) есть несколько оберток для более удобного
использования , но методы add и get лучше
выполнять напрямую и в примерах ниже я покажу как это делать.
6) Ключ
соотвествует типу HASHTYPEKEY и может может иметь тип Zstring или
Wstring (указывается в самой реализации хеш таблицы в самом
начале)
7) Данные соответствуют типу HASHTYPEVALUE и могут быть
любого типа. Тип по умолчанию - Zstring. Если HASHTYPEVALUE имеет тип ANY, то в
таблице хранится только указатель. Если HASHTYPEVALUE другого типа, то
сохраняются данные по указателю. В таблице без проблем сохраняются данные для
встроенных типов FreeBasic, кроме STRING. Вместо типа STRING используйте Zstring
Ptr. В таблице также могут храниться данные пользовательских типов,
но корректность данных будет только в том случае, если их
размер вычисляется во время компиляции.
Сам код моей реализации:
'-------- set type for key and value ----- Type HASHTYPEKEY As Zstring Type HASHTYPEVALUE As Zstring '----------------------------------------- Extern "C" Declare Function crt_memcpy Alias "memcpy"(As Any Ptr,As Any Ptr, As Integer) As Any Ptr End Extern Type THASHDATA pszKey As HASHTYPEKEY Ptr = 0 pszValue As HASHTYPEVALUE Ptr = 0 End Type Type TLIST Extends THASHDATA As TList Ptr p = 0 End Type Type TGLLIST As TList Ptr pFirst = 0 As TList Ptr pEnd = 0 Declare Sub add_last (pszKey As HASHTYPEKEY Ptr , pszValue As HASHTYPEVALUE Ptr) Declare Sub delete_element (pszKey As HASHTYPEKEY Ptr) Declare Function get_element ( pszKey As HASHTYPEKEY Ptr) As TList Ptr End Type Sub TGLLIST.add_last (pszKey As HASHTYPEKEY Ptr , pszValue As HASHTYPEVALUE Ptr) Dim As TList Ptr nw = Allocate(Sizeof(TList)) If nw = 0 Then Exit Sub nw->pszKey = pszKey nw->pszValue = pszValue nw->p = 0 If pFirst = 0 Then pFirst = nw pEnd = pFirst Else pEnd->p = nw pEnd = nw Endif End Sub Sub TGLLIST.delete_element (pszKey As HASHTYPEKEY Ptr) Dim As TList Ptr pTemp = pFirst, pTempOld pTempOld = pTemp While pTemp If *(pTemp->pszKey) = *pszKey Then If pTemp->p <> 0 Then If pTemp = pFirst Then pFirst = pTemp->p Else pTempOld->p = pTemp->p Endif Else If pTemp = pFirst Then pFirst = 0 pEnd = 0 Else pTempOld->p = 0 pEnd = pTempOld Endif Endif Deallocate(pTemp->pszKey) #IF Typeof(HASHTYPEVALUE) = Typeof(Any) #ELSE Deallocate(pTemp->pszValue) #ENDIF Deallocate(pTemp) Exit While Endif pTempOld = pTemp pTemp = pTemp->p Wend End Sub Function TGLLIST.get_element ( pszKey As HASHTYPEKEY Ptr) As TList Ptr Dim As TList Ptr pTemp = pFirst Dim As Long iLen = Len(*pszKey) While pTemp If Len(*(pTemp->pszKey)) = iLen Andalso *(pTemp->pszKey) = *pszKey Then Return pTemp Endif pTemp = pTemp->p Wend End Function Type THASHITEM Extends THASHDATA pList As TGLLIST Ptr iFdelete As Long End Type Type THASHTABLE pArray As THASHITEM Ptr iCountItem As Ulong iSize As Ulong Declare Sub delete_item(pszKey As HASHTYPEKEY Ptr) Declare Sub clear_table() Declare Sub free_table() Declare Sub Add(pszKey As HASHTYPEKEY Ptr , pszValue As HASHTYPEVALUE Ptr) Declare Sub add_rehash(pNew As THASHITEM Ptr , pszKey As HASHTYPEKEY Ptr , pszValue As HASHTYPEVALUE Ptr) Declare Function get_value(pszKey As HASHTYPEKEY Ptr) As HASHTYPEVALUE Ptr Declare Function hash_horner(psz As HASHTYPEKEY Ptr) As Long Declare Sub rehash() End Type Sub THASHTABLE.delete_item(pszKey As HASHTYPEKEY Ptr) Dim As Long iIndex = hash_horner(pszKey) If pArray[iIndex].pszKey Then If pArray[iIndex].iFdelete = 0 Andalso *(pArray[iIndex].pszKey) = *pszKey Then Deallocate(pArray[iIndex].pszKey) #IF Typeof(HASHTYPEVALUE) = Typeof(Any) #ELSE Deallocate(pArray[iIndex].pszValue) #ENDIF pArray[iIndex].iFdelete = 1 Else If pArray[iIndex].pList Then pArray[iIndex].pList->delete_element(pszKey) Endif Endif iCountItem-=1 If iCountItem*4 < iSize Then rehash() Endif Endif End Sub Sub THASHTABLE.clear_table() For i As Long = 0 To iSize If pArray[i].pszKey Then If pArray[i].iFdelete = 0 Then Deallocate(pArray[i].pszKey) #IF Typeof(HASHTYPEVALUE) = Typeof(Any) #ELSE Deallocate(pArray[i].pszValue) #ENDIF pArray[i].pszKey = 0 pArray[i].pszValue = 0 Endif If pArray[i].pList Then Dim As TList Ptr pTemp , pTemp2 pTemp = pArray[i].pList->pFirst pTemp2 = pArray[i].pList->pFirst While pTemp pTemp2 = pTemp->P Deallocate(pTemp->pszKey) #IF Typeof(HASHTYPEVALUE) = Typeof(Any) #ELSE Deallocate(pTemp->pszValue) #ENDIF Deallocate(pTemp) pTemp = pTemp2 Wend Deallocate(pArray[i].pList) pArray[i].pList = 0 Endif Endif Next iCountItem = 0 rehash() End Sub Sub THASHTABLE.free_table() Deallocate(pArray) End Sub Sub THASHTABLE.Add(pszKey As HASHTYPEKEY Ptr , pszValue As HASHTYPEVALUE Ptr) If pszKey = 0 Orelse pszValue = 0 Then ? "Invalidate value!" Exit Sub Endif If iCountItem > (iSize Shr 1) Andalso iCountItem < 32770 Then reHash() Endif Dim As Long iIndex = hash_horner(pszKey) If pArray[iIndex].pszKey = 0 Orelse pArray[iIndex].iFdelete Then pArray[iIndex].pszKey = Allocate((Len(*pszKey)+1)*Sizeof(HASHTYPEKEY)) *(pArray[iIndex].pszKey) = *pszKey #IF Typeof(HASHTYPEVALUE) = Typeof(Any) pArray[iIndex].pszValue = pszValue #ELSEIF Typeof(HASHTYPEVALUE) = Typeof(Wstring) pArray[iIndex].pszValue = Allocate((Len(*pszValue)+1)*Sizeof(HASHTYPEVALUE)) *(pArray[iIndex].pszValue) = *pszValue #ELSEIF Typeof(HASHTYPEVALUE) = Typeof(Zstring) pArray[iIndex].pszValue = Allocate((Len(*pszValue)+1)*Sizeof(HASHTYPEVALUE)) *(pArray[iIndex].pszValue) = *pszValue #ELSE pArray[iIndex].pszValue = Allocate(Sizeof(HASHTYPEVALUE)) crt_memcpy(pArray[iIndex].pszValue , pszValue , Sizeof(HASHTYPEVALUE)) #ENDIF If pArray[iIndex].iFdelete Then pArray[iIndex].iFdelete = 0 Else If *(pArray[iIndex].pszKey) = *pszKey Then #IF Typeof(HASHTYPEVALUE) = Typeof(Zstring) *(pArray[iIndex].pszValue) = *pszValue #ELSEIF Typeof(HASHTYPEVALUE) = Typeof(Wstring) *(pArray[iIndex].pszValue) = *pszValue #ELSEIF Typeof(HASHTYPEVALUE) = Typeof(Any) pArray[iIndex].pszValue = pszValue #ELSE crt_memcpy(pArray[iIndex].pszValue , pszValue , Sizeof(HASHTYPEVALUE)) #ENDIF Exit Sub Endif If pArray[iIndex].pList = 0 Then pArray[iIndex].pList = Callocate(Sizeof(TGLLIST)) Endif Dim As HASHTYPEKEY Ptr pTempKey = Allocate((Len(*pszKey)+1)*Sizeof(HASHTYPEKEY)) *pTempKey = *pszKey #IF Typeof(HASHTYPEVALUE) = Typeof(Any) pArray[iIndex].pList->add_last(pTempKey , pszValue) #ELSEIF Typeof(HASHTYPEVALUE) = Typeof(Wstring) Dim As HASHTYPEVALUE Ptr pTempValue = Allocate((Len(*pszValue)+1)*Sizeof(HASHTYPEVALUE)) *pTempValue = *pszValue pArray[iIndex].pList->add_last(pTempKey , pTempValue) #ELSEIF Typeof(HASHTYPEVALUE) = Typeof(Zstring) Dim As HASHTYPEVALUE Ptr pTempValue = Allocate((Len(*pszValue)+1)*Sizeof(HASHTYPEVALUE)) *pTempValue = *pszValue pArray[iIndex].pList->add_last(pTempKey , pTempValue) #ELSE Dim As HASHTYPEVALUE Ptr pTempValue = Allocate(Sizeof(HASHTYPEVALUE)) crt_memcpy(pTempValue , pszValue , Sizeof(HASHTYPEVALUE)) pArray[iIndex].pList->add_last(pTempKey , pTempValue) #ENDIF Endif iCountItem+=1 End Sub Sub THASHTABLE.add_rehash(pNew As THASHITEM Ptr , pszKey As HASHTYPEKEY Ptr , pszValue As HASHTYPEVALUE Ptr) Dim As Long iIndex = hash_horner(pszKey) If pNew[iIndex].pszKey = 0 Then pNew[iIndex].pszKey = pszKey pNew[iIndex].pszValue = pszValue Else If pNew[iIndex].pList = 0 Then pNew[iIndex].pList = Callocate(Sizeof(TGLLIST)) Endif pNew[iIndex].pList->add_last(pszKey , pszValue) Endif End Sub Function THASHTABLE.get_value(pszKey As HASHTYPEKEY Ptr) As HASHTYPEVALUE Ptr If pszKey = 0 Then ? "Invalidate value!" Return 0 Endif Dim As Long iIndex = hash_horner(pszKey) If (pArray[iIndex].pszKey Andalso pArray[iIndex].iFdelete = 0) Andalso *(pArray[iIndex].pszKey) = *pszKey Then Return pArray[iIndex].pszValue Else If pArray[iIndex].pList Then Dim As TList Ptr p = pArray[iIndex].pList->get_element(pszKey) If p Then Return p->pszValue Else Return 0 Endif Endif Endif End Function Function THASHTABLE.hash_horner(psz As HASHTYPEKEY Ptr) As Long Dim As Ulong iResult For i As Long = 0 To Len(*psz)-1 iResult = (11 * iResult + (*psz)[i]) Next Return iResult Mod iSize End Function Sub THASHTABLE.rehash() Dim As Long iOldSize = iSize If iCountItem > 32768 Then iSize = 131072 Elseif iCountItem > 16384 Then iSize = 65536 Elseif iCountItem > 8192 Then iSize = 32768 Elseif iCountItem > 4096 Then iSize = 16384 Elseif iCountItem > 2048 Then iSize = 8192 Elseif iCountItem > 1024 Then iSize = 4096 Elseif iCountItem > 512 Then iSize = 2048 Elseif iCountItem > 256 Then iSize = 1024 Elseif iCountItem > 128 Then iSize = 512 Elseif iCountItem > 64 Then iSize = 256 Elseif iCountItem > 32 Then iSize = 128 Elseif iCountItem > 16 Then iSize = 64 Endif Dim As THASHITEM Ptr pNew = Callocate(Sizeof(THASHITEM)*(iSize+4)) For i As Long = 0 To iOldSize If pArray[i].pszKey Then If pArray[i].iFdelete = 0 Then add_rehash(pNew , pArray[i].pszKey , pArray[i].pszValue) Endif If pArray[i].pList Then Dim As TLIST Ptr pTemp = pArray[i].pList->pFirst , pTemp2 While pTemp add_rehash(pNew , pTemp->pszKey , pTemp->pszValue) pTemp2 = pTemp->P Deallocate(pTemp) pTemp = pTemp2 Wend Deallocate(pArray[i].pList) Endif Endif Next Deallocate(pArray) pArray = pNew End Sub Function HashTableCreate() As Any Ptr Dim p As THASHTABLE Ptr = Allocate(Sizeof(THASHTABLE)) p->iCountItem = 0 p->iSize = 64 p->pArray = Callocate(Sizeof(THASHITEM)*(p->iSize+4)) Return p End Function Sub HashTableAdd(pTable As Any Ptr, pKey As HASHTYPEKEY Ptr , pValue As HASHTYPEVALUE Ptr) Cast(THASHTABLE Ptr , pTable)->Add(pKey , pValue) End Sub Function HashTableGet(pTable As Any Ptr, pKey As HASHTYPEKEY Ptr) As HASHTYPEVALUE Ptr Return Cast(THASHTABLE Ptr , pTable)->get_value(pKey) End Function Sub HashTableDeleteItem(pTable As Any Ptr, pKey As HASHTYPEKEY Ptr) Cast(THASHTABLE Ptr , pTable)->delete_item(pKey) End Sub Sub HashTableClear(pTable As Any Ptr) Cast(THASHTABLE Ptr , pTable)->clear_table() End Sub Sub HashTableFree(pTable As Any Ptr) Cast(THASHTABLE Ptr , pTable)->free_table() End Sub Function HashTableSize(pTable As Any Ptr) As Long Return Cast(THASHTABLE Ptr , pTable)->iSize End Function Function HashTableCount(pTable As Any Ptr) As Long Return Cast(THASHTABLE Ptr , pTable)->iCountItem End Function
Теперь примеры:
Пример 1 (полностью с обертками)
По умолчанию
тип для ключа и данных zstring. Если в самом верху реализации , вы замените
строки:
Type HASHTYPEKEY As Zstring
Type HASHTYPEVALUE As
Zstring
так:
Type HASHTYPEKEY As Wstring
Type HASHTYPEVALUE As
Wstring
тогда можно будет использовать строки UNICODE. Причем вы можете
для ключа оставить тип ZSTRING , а для данных WSTRING или
наоборот.
Dim As Any Ptr pTable = HashTableCreate() For i As Long = 0 To 10 HashTableAdd(pTable , "Key" & i , "Value" & i) Next ? ? "Items count="; HashTableCount(pTable) ? For i As Long = 0 To 10 ? *HashTableGet(pTable , "Key" & i) Next ? ? "delete Key5" HashTableDeleteItem(pTable , "Key5") ? ? "Items count="; HashTableCount(pTable) ? For i As Long = 0 To 10 ? *HashTableGet(pTable , "Key" & i) Next HashTableClear(pTable) HashTableFree(pTable) Sleep
пример 2 (тоже самое , но методы add и get вызываются напрямую ;
чуточку , но работает быстрее):
Dim As THASHTABLE Ptr pTable = HashTableCreate() For i As Long = 0 To 10 pTable->Add("Key" & i , "Value" & i) Next ? ? "Items count="; HashTableCount(pTable) ? For i As Long = 0 To 10 ? *pTable->get_value("Key" & i) Next ? ? "delete Key5" pTable->delete_item("Key5") ? ? "Items count="; pTable->iCountItem ? For i As Long = 0 To 10 ? *pTable->get_value("Key" & i) Next HashTableClear(pTable) HashTableFree(pTable) Sleep
Пример 3
В самом верху реализации таблицы пропишите это:
'-------- set type for key and value ----- Type MYTYPE As zstring*10 zs As Long i End Type Type HASHTYPEKEY As Zstring Type HASHTYPEVALUE As MYTYPE '-----------------------------------------И далее сам пример:
Dim As THASHTABLE Ptr pTable = HashTableCreate() Scope Dim As MYTYPE mp mp.zs = "one" mp.i = 10 pTable->Add("Key1" , @mp ) mp.zs = "two" mp.i = 20 pTable->Add("Key2" , @mp ) End Scope For i As Long = 1 To 2 Dim As MYTYPE Ptr p = pTable->get_value("Key" & i) ? p->zs ? p->i Next HashTableClear(pTable) HashTableFree(pTable) Sleep
Пример 4
В самом верху реализации таблицы пропишите это:
'-------- set type for key and value ----- Type HASHTYPEKEY As Zstring Type HASHTYPEVALUE As Any '-----------------------------------------
И далее сам пример (данные не копируются в таблицу , только указатели на данные):
Dim As THASHTABLE Ptr pTable = HashTableCreate() Dim As Long m(3) = {1,2,3,4} For i As Long = 0 To 3 pTable->Add("Key" & i , @m(i)) Next For i As Long = 0 To 3 Dim As Long Ptr p = pTable->get_value("Key" & i) ? *p Next HashTableClear(pTable) HashTableFree(pTable) Sleep