Is Dictionary dot syntax (my_dictionary.my_key) compatible with StringName keys?

Godot Version

4.4

Question

When accessing dictionaries you can use the shorter “dot syntax”

my_dictionary.my_key = "Hello"
var my_string:String = my_dictionary.my_key

And in the documentation this is described as the same as:

my_dictionary["my_key"] = "String Value"
var my_string:String = my_dictionary["my_key"]

However, since Strings and StringName are often converted back and forth I’m wondering if this is true, and my_dictionary.my_key = "String Value" is actually setting a String “my_key”, or if it’s setting a StringName &“my_key”?

And if I have a StringName &“my_key”, is my_dictionary.my_key the same as my_dictionary[&"my_key"], or does it add additional conversion back/forth to Strings?

It’s pretty easy to just try it yourself.

	var dict = {}
	dict["a"] = 1
	dict[&"b"] = 2
	for key in dict:
		print(type_string(typeof(key)), " ", key, " = ", dict[key])
	print(dict.a, " ", dict["a"], " ", dict[&"a"])
	print(dict.b, " ", dict["b"], " ", dict[&"b"])

Output:

String a = 1
String b = 2
1 1 1
2 2 2
1 Like

Thanks for the examples and testing, this really seems like a bug of some sort. Using StringName keys should be substancially faster since comparing StringNames is much faster than comparing Strings, so it would make no sense to convert StringNames to Strings to use as the actual keys.

When posting this question I was sure this would work and be faster, and only wanted to know if it would be automatic with the dot syntax, or if I’d need to use a more explicit StringName syntax.

I don’t have access to a 4.4 installation right now, so that might have changed with the typed dictionaries changes, but running a simple performance test on 4.3 like so:

var dict: Dictionary
var my_var:int 
var start_time:int 
	
start_time = Time.get_ticks_usec()
for i:int in 10000:
	dict["b"] = 2
	my_var = dict["b"]
print("get set String key dict time: "+str(Time.get_ticks_usec()-start_time)+"usec")
	
start_time = Time.get_ticks_usec()
for i:int in 10000:
	dict[&"c"] = 2
	my_var = dict[&"c"]
print("get set StringName dict time: "+str(Time.get_ticks_usec()-start_time)+"usec")

I get a result where the StringName getting and setting is almost twice as slow (I would have expected it to be 10x faster). Which I guess implies there is some conversion to/from String when using StringName keys? Has this changed on 4.4?

Yes, StringNames get converted to Strings automatically, as seen in my first code sample. And no, this hasn’t changed with 4.4 (I assumed you use 4.4 as per Godot Version in your first post).

Just got aware that I had an error in the typed Dictionary code, I removed it.

Actually, StringName Dictionaries work in 4.4, so you just have to update.

	var dict: Dictionary[StringName, int]
	dict[&"a"] = 1
	dict["b"] = 2
	for d in dict:
		prints(type_string(typeof(d)), d)
	return

outputs

StringName a
StringName b
1 Like

Right, did a test now on 4.4 and there at least the lower performance for using StringName keys is now gone. Using a simple key like a single letter like in the example above now results in about the same performance.

However, using longer keys and a couple more entries in the dictionaries using StringName keys now results in almost double the performance using this test code:

var string_dict: Dictionary[String,int]
	var my_var:int 
	var start_time:int 
	string_dict["another key"] = 1
	string_dict["a third key"] = 3
	var string_key:String = "A substancially longer key name"
	
	start_time = Time.get_ticks_usec()
	for i:int in 10000:
		string_dict[string_key] = 2
		my_var = string_dict[string_key]
	print("String key dictionary time: "+str(Time.get_ticks_usec()-start_time)+"usec")
	
	var stringname_dict: Dictionary[StringName,int]
	stringname_dict[&"another key"] = 1
	stringname_dict[&"a third key"] = 3
	var stringname_key:StringName = &"A substancially longer key name"
	
	start_time = Time.get_ticks_usec()
	for i:int in 10000:
		stringname_dict[stringname_key] = 2
		my_var = stringname_dict[stringname_key]
	print("StringName key dictionary time: "+str(Time.get_ticks_usec()-start_time)+"usec")

And, coming back to my original question, it also seems like with 4.4 it’s now been changed to that dictionary keys assigned using the dot syntax are added as StringName keys. So if you’re using that syntax you should get the performance benefit without any changes.

1 Like