You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android按文件名排序报IllegalArgumentException比较方法违反通用约定问题咨询

问题原因

你遇到的IllegalArgumentException: Comparison method violates its general contract!异常是JDK的TimSort排序算法抛出的,当自定义Comparator违反了以下三个核心约定时就会触发:

  1. 对称性:compare(a,b) == -compare(b,a)必须始终成立
  2. 自反性:compare(a,a) == 0必须始终成立
  3. 传递性:如果compare(a,b)>0且compare(b,c)>0,则compare(a,c)>0必须成立

你写的FileNameComparator存在多个违反约定的逻辑缺陷:

缺陷1:前导0处理逻辑完全错误

进入数字区域的前导0跳过逻辑中,循环判断用的是进入分支前缓存的cLeft/cRight值,i/j递增后没有重新获取当前位置的字符:

// 错误代码
while (i < lenLeft && cLeft == '0') i++
while (j < lenRight && cRight == '0') j++

比如字符串是"0012",第一次取的cLeft是'0',i递增后判断条件还是用原来的cLeft == '0',会直接把i加到字符串长度越界,完全没有实现跳过前导0的效果。

缺陷2:循环结束后的返回逻辑错误

遍历到任意字符串结束后,你直接返回两个字符串当前最后一个位置的字符差值:

// 错误代码
if (i == lenLeft) i--
if (j == lenRight) j--
return csLeft[i] - csRight[j]

这会导致大量完全不相等的字符串被判定为相等,比如compare("a", "aa")会返回0,直接违反对称性约定;对于长度不同的数字字符串也会得到完全错误的结果,比如compare("12", "123")的返回值完全由最后一个字符决定,而不是数字的实际大小。

缺陷3:数字长度判断逻辑缺失

当两个数字字符串长度不同时,你没有优先判定长度更长的数字更大,比如"123"和"45",你的逻辑刚好得到了正确结果,但如果是"123"和"12",就会得到错误的比较结果。

可复现异常的测试用例

以下代码可以100%触发你遇到的异常:

fun testComparatorCrash() {
    val comparator = FileNameComparator()
    val testList = listOf("1", "01", "001", "2", "02", "a", "aa", "b", "bb")
    Collections.sort(testList, comparator)
}

单独调用比较方法也可以直接验证约定违反:

// 输出0,两个完全不同的字符串被判定为相等,违反约定
println(FileNameComparator().compare("a", "aa"))
修复建议
  1. 修复前导0处理逻辑,每次i/j递增后重新读取当前位置的字符判断是否是前导0
  2. 循环结束后的返回逻辑优先判断长度:如果仍在数字区域,长度更短的数字更小;如果不在数字区域,长度更短的字符串更小
  3. 数字比较时优先比较有效数字的长度,长度更长的数字更大,再逐位比较相同位置的差值
  4. 所有逻辑分支都要保证compare(a,b) == -compare(b,a)成立

内容的提问来源于stack exchange,提问作者Shouheng Wang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.12 01:00:02