Skip to content

字典

字典是一个关联容器,包含值(Value)和唯一的键(Key)的引用。

添加新条目时,字典会保持插入顺序。在其他编程语言中,这种数据结构有时也称为哈希表(Hash Table)或关联数组(Associative Array)

同一个值可能对应不同的键,但同一个键只会有一个对应的值,在字典中键是唯一的不允许重复的,这种键值对应的一个条目,我们称之为键值对(Key-Value Pair)

像是数组中的值,我们将其称之为“元素(element)”

而字典中的键值对,我们除了称之为“元素(element)”,也可以将其称之为“条目(entry)”

因此,也可以借助字典实现令函数返回多个值

gdscript
func try_parse_to_int(s:String) -> Dictionary[String, Variant]:
    return {
        "success" : s.is_valid_float(), 
        "result" : s.to_int()
    }

特别的是,!===运算符并非比较字典的引用,而是比较的内容。当字典条目和大小相同时==返回true!=返回false,即使条目的顺序并不相同

创建

字典使用大括号{}创建,在创建时在大括号内放置用逗号分割的一对对键值(键 : 值)列表就可以添加初始键值对

gdscript
var my_dict = {} # 创建空字典。

var dict_variable_key = "Another key name"
var dict_variable_value = "value2"
var another_dict = {
    "Some key name": "value1",
    dict_variable_key: dict_variable_value,
}

var points_dict = {"White": 50, "Yellow": 75, "Orange": 100}

字典可以使用任意类型的数据常量或变量用作键值对的键或值,但最常用的还是使用字符串唯一字符串整数来作为字典的键,因为这样便于访问和数据交换

除了常规的语法,GDScript还支持使用Lua风格的Table语法来创建字典。

使用=而非:,且字符串键不使用引号

需要注意的是,这种形式编写的键和GDScript标识符一样不能以数字开头,并且无法使用已有的变量的值作为键,每一个键都是一个字符串字面量

gdscript
var d = {
    test22 = "value",
    some_key = 2,
    other_key = [2, 3, 4],
    more_key = "Hello"
}

字典允许包含不同类型的键和值,也允许嵌套结构

gdscript
var d = {4: 5, "A key": "A value", 28: [1, 2, 3]}

var my_dict = {
    "String Key": 5,
    4: [1, 2, 3],
    7: "Hello",
    "sub_dict": {"sub_key": "Nested value"},
}

访问

可以通过键来访问字典中对应的值

gdscript
var d = {4: 5, "A key": "A value", 28: [1, 2, 3]}
print(d[4]) # 输出 5
print(d["A key"]) # 输出 "A value"
print(d[28]) # 输出 [1, 2, 3]

访问不存在的键时将引发错误!

你还可以在访问时,取出对应的值

这方面同数组一样,键值对的值都是传递的副本,修改其值不会影响到原始键值对,如果值是对象,修改属性会影响键值对中的对象,但为其赋予一个新的实例不会影响键值对中的对象

gdscript
var value = d["A Key"]
value = "New value"
print(value) # 输出 "New value"
print(d["A Key"]) # 输出 "A value"

除此之外,如果字典的键是标准的标识符,并且类型为字符串,你也可以使用点语法来访问,否则只能使用方括号来访问

除了在方括号中填入字面量,你也可以使用变量或表达式;同样地,使用变量访问时,也无法使用点语法

不要混用点语法和方括号语法,这样会降低代码可读性

gdscript
@export_enum("White", "Yellow", "Orange") var my_color: String
var points_dict = {"White": 50, "Yellow": 75, "Orange": 100, "Alice Blue" : 120}

print(points_dict.White) # 输出 50
print(points_dict.Yellow) # 输出 75
print(points_dict.Orange) # 输出 100
print(points_dict[my_color]) # 不能使用点语法,因为`my_color`是变量
print(points_dict["Alice Blue"]) # 该条目不能使用点语法,因为键不是合法标识符

赋值

如果字典不是只读的,向字典添加条目时,像已有键一样访问并赋值即可

字典不允许存在重复的键,因此,如果键存在于字典中则会修改其值,如果不存在则会新建条目

新的条目会追加到字典最末尾

gdscript
var points_dict = {"White": 50, "Yellow": 75, "Orange": 100}
points_dict["Blue"] = 150 # 将 "Blue" 添加为键,并将 150 赋为它的值。
print(points_dict) # 输出 { "White": 50, "Yellow": 75, "Orange": 100, "Blue": 150 }

也可以使用点语法进行赋值,不过点语法赋值新增的键其类型是唯一字符串(&"")

gdscript
points_dict.Red = 120
print(points_dict) # { "White": 50, "Yellow": 75, "Orange": 100, "Blue": 150, &"Red": 100 }

不写赋值号就是访问键,如果键不存在会报错;写赋值号并跟上一个表达式就是修改或添加键,如果键不存在不会报错

遍历

字典同样可以使用for语句进行遍历,需要注意的是,迭代变量的值是字典的键,而非字典的值,也不是整个键值对

gdscript
var groceries = {"Orange": 20, "Apple": 2, "Banana": 4}
for fruit in groceries:
        var amount = groceries[fruit]

不要在遍历时修改元素数量,可能会造成无法预知的行为!

传递字典(按引用)

和数组一样,字典同样按引用传递,其规则与其一致,此处不再重复赘述

为了避免“连带修改”的情况,必要时,可以使用Dictionary.duplicate方法来复制一份新的字典

嵌套结构

字典也允许嵌套结构,和数组一致,嵌套的数组和字典也是按引用传递

必要时可以使用深拷贝Dictionary.duplicate(true)来避免连带修改嵌套的字典和数组

类型化字典

仅限Godot4.4或更高版本

与数组相同,字典也支持类型化,在开发时为开发者提供类型安全,其规则与类型化数组一致,通过Dictionary[KeyType, ValueType]指定

gdscript
var my_dict : Dictionary[String, int] = {"Apple" : 10, "Orange" : 12}

同样,不能直接将类型化字典赋值给不同类型的类型化字典,即使该类型是字典所接收类型的子类型

必要情况下可以使用Dictionary.assign方法赋值并进行类型转换

无类型的字典Dictionary等效于Dictionary[Variant, Variant]

类型化字典无法嵌套类型化集合:如Dictionary[String, Array[int]]是不被允许的

练习

  1. 创建一个字典,保存三种水果的价格,并打印“Apple”的价格

    gdscript
    # 示例结构:
    # {
    #   "Apple": 10,
    #   "Banana": 5,
    #   "Orange": 8
    # }
  2. 判断字典中是否有键 "Pineapple",如果有,就什么也不做,如果没有,就添加它并设定价格为12

  3. 有一个库存字典如下,请遍历这个字典,并将每种物品的数量加1。

    gdscript
    var stock = {
        "Potion": 3,
        "Elixir": 1,
        "Ether": 5
    }
  4. 创建一个函数 filter_expensive(dict, price_limit) 它接收一个价格字典和一个价格上限,返回一个只包含价格高于上限的物品的新字典。

    示例输入:

    gdscript
    var shop_items = {
        "Wooden Sword": 50,
        "Silver Sword": 150,
        "Potion": 20,
        "Shield": 100
    }

    调用 filter_expensive(shop_items, 100) 应该返回:

    gdscript
    {
        "Silver Sword": 150
    }
  5. 有一个 RPG 游戏玩家背包结构如下,编写一段代码,将金币加50,药水数量+1,并将最大生命值调成120。

    gdscript
    var player_inventory = {
        "items": {"Potion": 2, "Ether": 1},
        "gold": 150,
        "stats": {"hp": 100, "mp": 30}
    }
  6. 创建一个函数 deep_copy_and_upgrade(dict)实现以下逻辑

    • 深拷贝原始字典(不影响原字典)

    • 为每一项 stats 增加 10

    • 并返回这个新字典

    gdscript
    var stats = {
        "hp": 100,
        "mp": 50,
        "str": 20
    }