• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Hashtable Maximum?

Status
Not open for further replies.
Level 4
Joined
Jan 27, 2016
Messages
89
So I've been thinking about creating a hashtable which I'd use to save all of a units stats (which cannnot directly be gotten) at game start so I can get them later, however I thought about organizing them a little better and essentially using larger numbers in the hashtables

TLDR is there any difference between saving something in a hashtable as 10000 of temp_integer, compared to saving it as 10 of temp_integer?
 

Dr Super Good

Spell Reviewer
Level 65
Joined
Jan 18, 2005
Messages
27,290
TLDR is there any difference between saving something in a hashtable as 10000 of temp_integer, compared to saving it as 10 of temp_integer?
There is a marginal performance increase due to reduction in collisions in the underlying bucket arrays. If the bucket array size was either dynamic or customizable this would not be a problem (feature request?).

Do note there is a maximum hashtable limit of 256 (or was it 255?). Hashtables also cannot be fully destroyed once created so dynamic hashtable creation must be implemented carefully so as to not run into the limit.
 
Level 13
Joined
Jan 2, 2016
Messages
978
Yet if you want to have more than 255 hashtable, you could use some system like this:
JASS:
library TableSpecial

  globals
  private hashtable T1 = InitHashtable()
  private hashtable T2 = InitHashtable()
  private constant string EMPTY = ""
  endglobals

  function CreateTableSpecial takes integer k1, integer k2 returns nothing
  local integer i = LoadInteger(T2, 0, 1)
  local integer c
  if k1 >= 0 and not HaveSavedInteger(T2, k1 + 1, k2) then
  if i == 0 then
  set i = LoadInteger(T2, 0, 0)
  call SaveInteger(T2, k1 + 1, k2, i)
  call SaveInteger(T2, 0, 0, i + 1)
  else
  set c = LoadInteger(T2, 0, i + 1)
  call RemoveSavedInteger(T2, 0, i + 1)
  call SaveInteger(T2, 0, 1, i - 1)
  call SaveInteger(T2, k1 + 1, k2, c)
  endif
  endif
  endfunction

  function DestroyTableSpecial takes integer k1, integer k2 returns nothing
  local integer i = LoadInteger(T2, 0, 1) + 1
  local integer c = LoadInteger(T2, k1 + 1, k2)
  if k1 >= 0 and HaveSavedInteger(T2, k1 + 1, k2) then
  call RemoveSavedInteger(T2, k1 + 1, k2)
  call FlushChildHashtable(T1, c)
  call SaveInteger(T2, 0, i + 1, c)
  set c = LoadInteger(T2, 0, 0)
  if c == i then
  call FlushChildHashtable(T2, 0)
  else
  call SaveInteger(T2, 0, 1, i)
  endif
  endif
  endfunction
  
  function FlushChildHashtableSpecial takes integer k1, integer k2 returns nothing
  local integer k4
  if HaveSavedInteger(T2, k1 + 1, k2) then
  set k4 = LoadInteger(T2, k1 + 1, k2)
  call FlushChildHashtable(T1, k4)
  endif
  endfunction

//! textmacro HasSavedNRemoveSaved takes NAME
  function HaveSaved$NAME$Special takes integer k1, integer k2, integer k3 returns boolean
  local integer k4
  if HaveSavedInteger(T2, k1 + 1, k2) then
  set k4 = LoadInteger(T2, k1 + 1, k2)
  return HaveSaved$NAME$(T1, k4, k3)
  endif
  return false
  endfunction

  function RemoveSaved$NAME$Special takes integer k1, integer k2, integer k3 returns nothing
  local integer k4
  if HaveSavedInteger(T2, k1 + 1, k2) then
  set k4 = LoadInteger(T2, k1 + 1, k2)
  call RemoveSaved$NAME$(T1, k4, k3)
  endif
  endfunction
//! endtextmacro

//! runtextmacro HasSavedNRemoveSaved("Integer")
//! runtextmacro HasSavedNRemoveSaved("Real")
//! runtextmacro HasSavedNRemoveSaved("Handle")
//! runtextmacro HasSavedNRemoveSaved("Boolean")
//! runtextmacro HasSavedNRemoveSaved("String")

//! textmacro SaveNLoad takes NAME, TYPE, HANDLE, VAL
  function Save$NAME$$HANDLE$Special takes integer k1, integer k2, integer k3, $TYPE$ val returns nothing
  local integer k4
  if HaveSavedInteger(T2, k1 + 1, k2) then
  set k4 = LoadInteger(T2, k1 + 1, k2)
  call Save$NAME$$HANDLE$(T1, k4, k3, val)
  endif
  endfunction
  
  function Load$NAME$$HANDLE$Special takes integer k1, integer k2, integer k3 returns $TYPE$
      local integer k4
      if HaveSavedInteger(T2, k1 + 1, k2) then
          set k4 = LoadInteger(T2, k1 + 1, k2)
          return Load$NAME$$HANDLE$(T1, k4, k3)
      endif
      return $VAL$
  endfunction
//! endtextmacro

//! runtextmacro SaveNLoad("Boolean","boolean","","false")
//! runtextmacro SaveNLoad("Str","string","","EMPTY")
//! runtextmacro SaveNLoad("Real","real","","0.00")
//! runtextmacro SaveNLoad("Integer","integer","","0")
//! runtextmacro SaveNLoad("Player","player","Handle","null")
//! runtextmacro SaveNLoad("Widget","widget","Handle","null")
//! runtextmacro SaveNLoad("Destructable","destructable","Handle","null")
//! runtextmacro SaveNLoad("Item","item","Handle","null")
//! runtextmacro SaveNLoad("Unit","unit","Handle","null")
//! runtextmacro SaveNLoad("Ability","ability","Handle","null")
//! runtextmacro SaveNLoad("Timer","timer","Handle","null")
//! runtextmacro SaveNLoad("Trigger","trigger","Handle","null")
//! runtextmacro SaveNLoad("TriggerCondition","triggercondition","Handle","null")
//! runtextmacro SaveNLoad("TriggerAction","triggeraction","Handle","null")
//! runtextmacro SaveNLoad("TriggerEvent","event","Handle","null")
//! runtextmacro SaveNLoad("Force","force","Handle","null")
//! runtextmacro SaveNLoad("Group","group","Handle","null")
//! runtextmacro SaveNLoad("Location","location","Handle","null")
//! runtextmacro SaveNLoad("Rect","rect","Handle","null")
//! runtextmacro SaveNLoad("BooleanExpr","boolexpr","Handle","null")
//! runtextmacro SaveNLoad("Sound","sound","Handle","null")
//! runtextmacro SaveNLoad("Effect","effect","Handle","null")
//! runtextmacro SaveNLoad("UnitPool","unitpool","Handle","null")
//! runtextmacro SaveNLoad("ItemPool","itempool","Handle","null")
//! runtextmacro SaveNLoad("Quest","quest","Handle","null")
//! runtextmacro SaveNLoad("QuestItem","questitem","Handle","null")
//! runtextmacro SaveNLoad("DefeatCondition","defeatcondition","Handle","null")
//! runtextmacro SaveNLoad("TimerDialog","timerdialog","Handle","null")
//! runtextmacro SaveNLoad("Leaderboard","leaderboard","Handle","null")
//! runtextmacro SaveNLoad("Multiboard","multiboard","Handle","null")
//! runtextmacro SaveNLoad("MultiboardItem","multiboarditem","Handle","null")
//! runtextmacro SaveNLoad("Trackable","trackable","Handle","null")
//! runtextmacro SaveNLoad("Dialog","dialog","Handle","null")
//! runtextmacro SaveNLoad("Button","button","Handle","null")
//! runtextmacro SaveNLoad("TextTag","texttag","Handle","null")
//! runtextmacro SaveNLoad("Lightning","lightning","Handle","null")
//! runtextmacro SaveNLoad("Image","image","Handle","null")
//! runtextmacro SaveNLoad("Ubersplat","ubersplat","Handle","null")
//! runtextmacro SaveNLoad("Region","region","Handle","null")
//! runtextmacro SaveNLoad("FogState","fogstate","Handle","null")
//! runtextmacro SaveNLoad("FogModifier","fogmodifier","Handle","null")
//! runtextmacro SaveNLoad("Hashtable","hashtable","Handle","null")

endlibrary
This system uses 2 hashtables to simulate a hashtable with 3 keys. You could use the 1-st key as "hashtable 1, 2, 3... etc" and use the other 2 keys the normal way.

And as for your question - nope it doesn't matter if you save stuff into 10-th key or into the 10000000-th key
 

Dr Super Good

Spell Reviewer
Level 65
Joined
Jan 18, 2005
Messages
27,290
Okay, but what about hashtables not being fully destroyable?
Once one is created, it cannot be destroyed in a way which frees it from the global hashtable limit. This means that creating a new Hashtable will work exactly 255 (or 256, depending on bounds) times before it returns null or something invalid.
You can destroy them withFlushParentHashtable.
This should just return it to initial state, not destroy it. This difference is important because dynamically created hashtables might be used to combat hashtable scaling problems in some data management structures.
 
Level 13
Joined
Jan 2, 2016
Messages
978
After using FlushParentHashtable - the hashtable becomes unusable, so you need to create a new one. I had some weird bug until I found that out :p
Anyways... you could easily check if you can't have more than 255(6) hashtables in total OR only simulatenous by doing:
JASS:
local integer i = 0
loop
    exitwhen i > 300
    set udg_Table = InitHashtable()
    call FlushParentHashtable(udg_Table)
    set i = i + 1
endloop
set udg_Table = InitHashtable()
call SaveInteger(udg_Table, 0, 0, 14)
call BJDebugMsg(I2S(LoadInteger(udg_Table, 0, 0)))
 

Rui

Rui

Level 41
Joined
Jan 7, 2005
Messages
7,551
Okay, my bad, I misunderstood. I thought we were saying there was a maximum of 256 entries per hashtable.

There's a maximum of 255 hashtables and 2^-31 to 2^31 possible indexes/entries (wish I had known earlier that negative indexes worked too q-q).
 
Level 26
Joined
Aug 18, 2009
Messages
4,099
concerning destruction: If it's not allowed to destroy them/it does not become free for another hashtable, just clear it and put it in some allocation recycler.

It can make sense to use a lot of hashtables. For example to divide the strain.

Even if a hashtable can store 2^32^2 entries per type, it does not mean that one hashtable can/should cover all tasks. For example imagine you want to save a 3d field, mapping values to R^3. While you may not have the need to assign all the values, the domain of definition is prone to explode.
 
Status
Not open for further replies.
Top