指南

JSON 数组对比实战:By Index、LCS、Unordered 到底差在哪

2026年10月3日 · 约 6 分钟

文档里对三种数组对比方法各有一段简介。用来选一个够用——但不足以预测 diff 返回时你到底会看到什么。这篇文章把同一份数据分别用三种方法跑一遍,展示每种方法的精确输出,因为它们的差异比名字听起来大得多。

先快速回顾机制(详见引擎原理):

  • By Index 把 a[i] 和 b[i] 配对,逐对递归。
  • LCS 用动态规划求出深度相等元素的最长公共子序列,序列之外的报告为插入或删除。
  • Unordered 把数组当作多重集合:base 的每个元素在 contrast 侧消耗一个深度相等的匹配。

同一个选项,三种截然不同的答案。

场景 1:中间插入一个元素

一条事件日志在位置 1 新增了一条记录:

{ "items": [1, 2, 3, 4] }
{ "items": [1, 5, 2, 3, 4] }

By Index 发现位置对齐被打乱,报告出一串级联:

valueChanged  items[1]
valueChanged  items[2]
valueChanged  items[3]
added         items[4]

一次插入,四条差异——技术上正确,实际上没用。插入点之后的一切都「变了」。

LCS 找到公共子序列 [1, 2, 3, 4],精确报告发生的事:

added  items[1]

Unordered 同样命中——base 的每个元素都找到了匹配,contrast 侧剩下一个:

added  items[1]

胜者:LCS 或 Unordered。除非位置本身真的有意义,否则 By Index 在这里只会制造噪声。

场景 2:纯乱序

同样的元素,不同的顺序:

{ "items": [1, 2, 3] }
{ "items": [3, 1, 2] }

By Index 把每个位置都报告为变更:

valueChanged  items[0]
valueChanged  items[1]
valueChanged  items[2]

LCS 保住它能保住的最长有序片段([1, 2]),剩下的报告为移动:

deleted  items[2]
added    items[0]

Unordered 什么都没说:

(无差异)

哪个答案正确完全取决于你的数据。部署流水线里步骤换了顺序?By Index 的警报是对的——顺序就是语义。博客文章的标签换了顺序?Unordered 的沉默是对的。这个场景最容易因为选错方法而要么被噪声淹没,要么漏掉真正的回归。

场景 3:元素被原地修改

这是文档没有明说的取舍。数组里一个对象的字段变了:

{ "users": [{ "id": 1, "role": "admin" }, { "id": 2, "role": "user" }] }
{ "users": [{ "id": 1, "role": "admin" }, { "id": 2, "role": "owner" }] }

By Index 递归进这一对,精确到字段:

valueChanged  users[1].role

LCS 按深度相等匹配元素——而 { "id": 2, "role": "user" } 和 { "id": 2, "role": "owner" } 并不深度相等。于是这个元素整个掉出公共子序列:

deleted  users[1]
added    users[1]

Unordered 的表现一样:

deleted  users[1]
added    users[1]

同一个索引,但没有任何关于内部什么变了的线索。如果你的数组里是对象、而且你关心字段级的变化,LCS 和 Unordered 会丢失信息——它们说不出「role 从 user 变成了 owner」,只能说「[1] 处少了点什么、多了点什么」。By Index 是唯一能保住字段级精度的方法。

场景 4:重复元素是被计数的,不是被忽略的

Unordered 常被描述成「集合对比」,但实现是多重集合语义:每个匹配一对一消耗。

{ "tags": [1, 2, 2, 3] }
{ "tags": [3, 1, 2] }

左边的两个 2 只能消耗掉右边的一个 2:

deleted  tags[2]

如果重复次数对你的数据有意义,这正是你想要的行为。如果你真的想要集合语义(「唯一值相同即可」),应该在对比前去重——或者清楚 Unordered 会把数量差异报告出来。

性能:隐藏的比较维度

三种方法的成本也不同,在大数组上会很明显:

方法时间复杂度说明
By IndexO(n) 次比较每对再各加一次递归比较
LCSO(m × n) 次深度相等判断DP 的每个格子都可能递归比较两棵完整的子树
Unordered最坏 O(m × n)每个未匹配的 base 元素都要扫描 contrast 侧

一百个元素的数组,怎么选都无感。但对一万个嵌套对象的数组,LCS 的 DP 表——每个格子一次完整递归比较——是真的贵,Unordered 的平方扫描也好不到哪去。By Index 线性增长,是唯一在任何规模下都几乎免费的方法。

睁着眼睛做选择

你的数组是…最佳方法你放弃了什么
位置语义(元组、时间序列、表格行)By Index插入会导致整体偏移——错位列表上全是噪声
有序但偶尔增删(日志、更新记录)LCS修改元素的字段级精度;O(m·n) 成本
无序集合(标签、权限、开关)Unordered顺序变化不可见;只能报告整个元素的增删

一句话总结:By Index 精确但脆弱,LCS 精简但肤浅,Unordered 平静但对顺序失明。

拿你自己的数组试试——方法开关就在设置里,切换后几秒钟就能重跑一次对比:立即对比两个 JSON 文件。