Android中对包含数字的字符串进行排序(Kotlin语法)

背景

有一组顺序错乱的字符串如下:

第10节课、第2节课、第1节课、第11节课、第100节课、第99节课、第101节课、第9节课

对字符串进行排序,期望结果肯定是这样的:

 第1节课、第2节课、第9节课、第10节课、第11节课、第99节课、第100节课、第101节课

通过字符串对比排序:

private fun test() {
	val array1: List<String> = listOf(
		"第10节课",
		"第2节课",
		"第1节课",
		"第11节课",
		"第100节课",
		"第99节课",
		"第101节课",
		"第9节课",
	)
	Collections.sort(array1,object:Comparator<String>{
		override fun compare(o1: String, o2: String): Int {
			return o1.compareTo(o2)
		}
	})
	println(array1)

}

实际结果是这样的: 

[第100节课, 第101节课, 第10节课, 第11节课, 第1节课, 第2节课, 第99节课, 第9节课]

比如“第100节课”和“第1节课”这组值,按照字符对比的话,前面相同,而"0"小于"节",自然是“第100节课”排在“第1节课”前面,但是这样的结果并不是我们预期的。

分析

看这组字符串的特点:

  • 包含数值
  • 数值前面部分是相同的

字符串排序,如果前面的字符都不一致,那么还没到数值部分就已经排好序了,就不需要再对比数值了。前面的字符相同,后面按照我们的预期排序的话,我们就需要将数值提取出来,按照数值大小做对比就可以了

方案

网上找到以下方案:

扫描二维码关注公众号,回复: 17814585 查看本文章

对Java中包含数字的字符串进行排序

此方案单纯提取了字符串中的数值,对比数值大小排序的,可以解决部分问题。但是如果我们的字符串是"第2-1-2节"这样的(包含多个数值),这个方案就不能处理了

兼容方案

按照数值将字符串拆分成数组,比如"第5章第100节课"拆分成"[第,5,章第,100,节课]",拆分后依次对比两个数组中的每一个值,如果都是数值,那么按照数值大小做对比,否则按照字符串对比。

可以兼容以下场景:

  • 无数值时按照字符串对比
  • 字符串中如果包含多组数值,可以正常排序

具体代码

/**
 * 包含数字的字符串进行比较(按照从小到大排序)
 */
fun compareString(string1: String?, string2: String?): Int {
	//拆分两个字符串
	val lstString1 = splitString(string1.orEmpty())
	val lstString2 = splitString(string2.orEmpty())
	//依次对比拆分出的每个值
	var index = 0
	while (true) {
		//相等表示两个字符串完全相等
		if (index >= max(lstString1.size, lstString2.size)) {
			return 0
		}
		val str1 = if (index < lstString1.size) lstString1[index] else ""
		val str2 = if (index < lstString2.size) lstString2[index] else ""
		//字符串相等则继续判断下一组数据
		if (str1 == str2) {
			index++
			continue
		}
		//是纯数字,比较数字大小
		if (isNum(str1) && isNum(str2)) {
			return if (str1.toInt() < str2.toInt()) -1 else 1
		}
		return if (str1 < str2) -1 else 1
	}
}

/**
 * 拆分字符串
 * 输入:第5章第100节课
 * 返回:[第,5,章第,100,节课]
 */
private fun splitString(string: String): List<String> {
	val matcher = Pattern.compile("([^0-9]+)|(\\d+)").matcher(string)
	val list = mutableListOf<String>()
	while (matcher.find()) {
		list.add(matcher.group())
	}
	return list
}

/**
 * 是否是纯数字
 */
private fun isNum(string: String): Boolean {
	return Pattern.compile("\\d+").matcher(string).matches()
}

案例

案例一:

 val array1: List<String> = listOf(
	"第10节课",
	"第2节课",
	"第1节课",
	"第11节课",
	"第100节课",
	"第99节课",
	"第101节课",
	"第9节课",
)
Collections.sort(array1,object:Comparator<String>{
	override fun compare(o1: String, o2: String): Int {
		return compareString(o1,o2)
	}
})
println(array1)

输出结果:

[第1节课, 第2节课, 第9节课, 第10节课, 第11节课, 第99节课, 第100节课, 第101节课]

案例二:

val array2: List<String> = listOf(
	"第1-1-2节",
	"第2-1-2节",
	"第2-2-3节",
	"第2-10-2节",
	"第10-2-3节",
)

Collections.sort(array2, object : Comparator<String> {
	override fun compare(o1: String, o2: String): Int {
		return compareString(o1, o2)
	}
})
println(array2)

输出结果:

[第1-1-2节, 第2-1-2节, 第2-2-3节, 第2-10-2节, 第10-2-3节]

案例三:

val array3: List<String> = listOf(
	"第1节课",
	"第10节课",
	"第9节课",
	"无数值",
	"第1-1-2节",
	"第2-1-2节",
	"第2-2-3节",
	"第2-10-2节",
	"第10-2-3节",
	"无数值二",
	"1数值在前",
	"10数值在前",
)

Collections.sort(array3, object : Comparator<String> {
	override fun compare(o1: String, o2: String): Int {
		return compareString(o1, o2)
	}
})
println(array3)

输出结果:

 [1数值在前, 10数值在前, 无数值, 无数值二, 第1-1-2节, 第1节课, 第2-1-2节, 第2-2-3节, 第2-10-2节, 第9节课, 第10-2-3节, 第10节课]


注:IOS中找到以下排序方案

在iOS中对包含字母数字单词的数组进行排序https://qa.1r1g.com/sf/ask/1453093981/兼容方案的排序结果就是对其的IOS官方API的这个排序结果。

Java中如果还有其他比较好的方案,欢迎指正...