字典存入数据库再读出来,顺序变了,怎么判断和原来的一样?

« 返回首页

一句话结论:字典读出后键的顺序变化是正常的(安卓按键名排序);直接用「等于」比较,只看键和值是否相同、不看顺序。

有用户问:新建一个字典,里面有若干键值对,存进数据库后再读出来,发现键值对的顺序和存进去时不一样了,像是按键名的字母排了序。这时想判断读出来的字典和新建的字典是否完全相同,有什么好办法?

先说结论:

  • 顺序变了是正常的(安卓上读出来按键名排序),数据没坏。
  • 直接用逻辑抽屉的「等于」。比较两个字典时只看内容:键一样、每个键对应的值一样就是「真」,不看顺序。值里有数字、小数、列表、子字典都能正确比较。
  • 之前有 bug,现已修复:安卓上值里有数字时「等于」会误判成「假」(MIT 原版同样有这个 bug);鸿蒙上字典存进微数据库,App 重启后读不回来。文末写了 bug 原因和修复前的兼容写法。

为什么顺序会变

微数据库保存字典时,先把字典转成 JSON 文本存起来,读的时候再把 JSON 文本转回字典。安卓上,转回字典这一步特意按键名排序,保证同样的数据每次读出来顺序都一样。云数据库、Firebase 数据库、Web客户端「用字典解码JSON文本」也是同一套转换。

所以存进去是:

{"name":"Tom","city":"BJ"}

安卓上读出来变成了(英文键按字母排,中文键按编码排):

{"city":"BJ","name":"Tom"}

鸿蒙上读出来的顺序和存入时一样。

实验一:顺序不同,「等于」照样是真

when Button1.Click() {
  var 原字典 = makeDict(pair("name", "Tom"), pair("city", "BJ"))
  TinyDB1.StoreValue("资料", 原字典)
  var 读出 = TinyDB1.GetValue("资料", makeDict())
  Label1.Text = join(读出, " 相同吗?", 原字典 == 读出)
}

跑起来看: 安卓上标签显示 {"city":"BJ","name":"Tom"} 相同吗?true,顺序变了,比较结果是「真」。鸿蒙上显示 {name=Tom, city=BJ} 相同吗?true,顺序没变,字典的显示格式和安卓不同,比较结果同样是「真」。

实验二:值里有数字、列表、子字典

when Button1.Click() {
  var 原字典 = makeDict(pair("name", "Tom"), pair("age", 12), pair("score", 9.5), pair("tags", [1, 2]), pair("sub", makeDict(pair("z", 1), pair("k", "v"))))
  TinyDB1.StoreValue("资料", 原字典)
  var 读出 = TinyDB1.GetValue("资料", makeDict())
  Label1.Text = join("相同吗?", 原字典 == 读出)
}

跑起来看: 标签显示 相同吗?true。

比较规则和比较单个值时一样:数字按数值比(12 和 12.0 相等,文本 "12" 和数字 12 也相等,iOS 上这两个不相等),子字典同样不看顺序,列表要看顺序([1, 2] 和 [2, 1] 不相等)。

实验三:内容真的不同时是假

把读出来的字典里的 age 改成 13 再比较:

when Button2.Click() {
  var 原字典 = makeDict(pair("name", "Tom"), pair("age", 12))
  TinyDB1.StoreValue("资料", 原字典)
  var 读出 = TinyDB1.GetValue("资料", makeDict())
  dictSet(读出, "age", 13)
  Label1.Text = join("相同吗?", 原字典 == 读出)
}

跑起来看: 标签显示 相同吗?false。多一个键、少一个键、键名不同,结果也都是「假」。

之前的 bug 与修复

已修复:下面两个 bug 在新版 AI伴侣 和编译服务中已修复,用户手机上要升级 AI伴侣,或用新版编译服务重新编译 App 才生效。还没升级时,按各平台的说明处理。

安卓:值里有数字时「等于」误判为不相等

现象:实验二显示 相同吗?false,内容明明一样,「等于」却判为不同。值只有文字、真假时没有这个问题。

原因:两个 12 在系统内部是不同的类型。积木里写的 12 是 App Inventor 自己的整数类型,从 JSON 文本解析出来的 12 是 Java 的整数类型。比较单个数字时,「等于」会换算成数值再比;但比较整个字典时,原来直接调用 Java 自带的字典比较,逐项严格比对,类型不同就当成不相等。

MIT 原版也有这个 bug:这段比较逻辑来自 MIT 原版 App Inventor,截至 2026 年 10 月,MIT 官方最新代码仍是这样,用原版同样会遇到。

修复:比较两个字典时,改为逐个键用「等于」同样的规则比较值,数字按数值比,子字典递归比较。本站版本已修复。

修复前的兼容写法:让两边都用 Web客户端「编码 JSON → 解码回字典」规范化一遍再比较,两边的数字就成了同一种类型。拖一个 Web客户端(通信连接 → Web客户端,属性不用改),用它的「编码JSON对象」和「用字典解码JSON文本」。

function 规范化(字典) {
  return Web1.JsonTextDecodeWithDictionaries(Web1.JsonObjectEncode(字典))
}

when Button1.Click() {
  var 原字典 = makeDict(pair("name", "Tom"), pair("age", 12), pair("score", 9.5), pair("tags", [1, 2]))
  TinyDB1.StoreValue("资料", 原字典)
  var 读出 = TinyDB1.GetValue("资料", makeDict())
  Label1.Text = join("相同吗?", 规范化(原字典) == 规范化(读出))
}

跑起来看: 修复前后都显示 相同吗?true。

两边都规范化,而不只是规范化新建的那个,是为了不用关心读出来的字典来自哪种数据库:同样的数据规范化之后一定一模一样。

不要比较两边的 JSON 文本:把两个字典分别「编码JSON对象」再比文本,顺序不同文本就不同。只要原字典不是恰好按键名顺序录入的,就会判成不同。

鸿蒙:字典存进微数据库,App 重启后读不回来

现象:字典存进微数据库后,App 运行期间能正常读出;App 重启后读出来就不再是字典,字典积木都取不到值。另外,「等于」比较两个字典时会看顺序。

原因:鸿蒙版保存字典时,把字典的内部结构原样转成了 JSON(形如 {"keys":["name"],"vals":["Tom"]}),而不是标准的 {"name":"Tom"},读回来时也没有转回字典。Web客户端「编码JSON对象」编码字典也是同样的问题,所以上面安卓的兼容写法在修复前的鸿蒙上也不可靠。「等于」比较字典时,原来是把两边转成显示文本再比,所以顺序不同就不相等。

修复:字典改为编码成标准 JSON,与安卓一致;微数据库读回时还原成字典;「等于」比较字典时只看内容,不看顺序。修复前存下的字典数据不会丢,升级后读出来会自动还原成字典。

修复前:没有积木层面的绕行办法,请升级鸿蒙 AI伴侣,或用新版编译服务重新编译 App。

如果需要保留顺序

字典本身不保证存取后的顺序。如果顺序有意义(比如要按录入顺序显示),有两种存法:

  • 存成「列表的列表」:每一项是 [键, 值],列表存取前后顺序不变。需要按键查值时,用列表抽屉的「键值对查询」,或者用字典抽屉的「键值对列表转换为字典」临时转成字典查。
  • 另存一份键的顺序:字典照常存,再单独存一个列表记录键的录入顺序,显示时按这个列表逐个取值。

小结

情况 办法
判断两个字典内容是否完全相同 直接「等于」,不看顺序
安卓修复前,值有数字时「等于」误判 两边都用 Web客户端「编码 → 解码」规范化,再「等于」
鸿蒙修复前,重启后字典读不回来 升级 AI伴侣 或重新编译,旧数据自动还原
需要保留键值对的顺序 存成列表的列表,或另存一份键的顺序
文档反馈