最近项目中需要比较客户端版本,项目之初使用String.compareTo()方法进行版本比较,客户端版本如:8.2.3,各级版本号都为数字且以’.’分割,当某一级别版本号大于等于10时,String.compareTo()方法返回错误。
一、String.compareTo()比较版本号
通过String.compareTo()比较不同的版本号,结果如下:
public class Test {
public static void main(String[] args) {
System.out.println("\"8.2.3\".compareTo(\"8.2.3\") : " + "8.2.3".compareTo("8.2.3"));
System.out.println("\"8.2.3\".compareTo(\"8.7.3\") : " + "8.2.3".compareTo("8.7.3"));
System.out.println("\"8.2.3\".compareTo(\"8.1.1\") : " + "8.2.3".compareTo("8.1.1"));
System.out.println("\"8.2.3\".compareTo(\"8.2.15\") : " + "8.2.3".compareTo("8.2.15"));
System.out.println("\"8.2.3\".compareTo(\"8.2.a\") : " + "8.2.3".compareTo("8.2.a"));
System.out.println("\"8.2.3\".compareTo(\"8.2.1.236\") : " + "8.2.3".compareTo("8.2.1.236"));
System.out.println("\"8.2.3\".compareTo(\"8.8.1.236\") : " + "8.2.3".compareTo("8.8.1.236"));
System.out.println("\"8.2.3\".compareTo(\"9.4\") : " + "8.2.3".compareTo("9.4"));
System.out.println("\"8.2.3\".compareTo(\"6.4\") : " + "8.2.3".compareTo("6.4"));
}
}
结果:
"8.2.3".compareTo("8.2.3") : 0
"8.2.3".compareTo("8.7.3") : -5
"8.2.3".compareTo("8.1.1") : 1
"8.2.3".compareTo("8.2.15") : 2
"8.2.3".compareTo("8.2.a") : -46
"8.2.3".compareTo("8.2.1.236") : 2
"8.2.3".compareTo("8.8.1.236") : -6
"8.2.3".compareTo("9.4") : -1
"8.2.3".compareTo("6.4") : 2
以上大部分比较结果正确,但是版本号8.2.3与8.2.15的比较结果出现错误,8.2.3的版本应该比8.2.15的版本小。
String.compareTo()源码如下,该方法比较逻辑为:按字符比较两个字符串,返回前k(两字符串中较短字符串的长度)个字符中第一个不同字符的差值,如果前k个字符都相同,则返回两个字符串的长度差值。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
...
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
...
}
二、正确的版本号比较方法
String.compareTo()方法适用于按字符比较的场景,不适用于版本号比较。我们使用到的客户端版本都是数字加分隔符(”.”)组合的,正确的方式应该按分隔符将版本号切分为各级版本,然后按级别比较版本号,如下:
public class Test {
public static void main(String[] args) {
Test test = new Test();
System.out.println("compare(\"8.2.3\", \"8.2.3\") : " + test.compare("8.2.3", "8.2.3"));
System.out.println("compare(\"8.2.3\", \"8.7.3\") : " + test.compare("8.2.3","8.7.3"));
System.out.println("compare(\"8.2.3\", \"8.1.1\") : " + test.compare("8.2.3","8.1.1"));
System.out.println("compare(\"8.2.3\", \"8.2.15\") : " + test.compare("8.2.3","8.2.15"));
System.out.println("compare(\"8.2.3\", \"8.2.1.236\") : " + test.compare("8.2.3","8.2.1.236"));
System.out.println("compare(\"8.2.3\", \"8.8.1.236\") : " + test.compare("8.2.3","8.8.1.236"));
System.out.println("compare(\"8.2.3\", \"9.4\") : " + test.compare("8.2.3","9.4"));
System.out.println("compare(\"8.2.3\", \"6.4\") : " + test.compare("8.2.3","6.4"));
}
public int compare(String v1, String v2) {
if(StringUtils.isBlank(v1) && StringUtils.isBlank(v2)) {
return 0;
} else if(StringUtils.isBlank(v1)) {
return -1;
} else if(StringUtils.isBlank(v2)) {
return 1;
}
int [] varr1 = getVersionArray(v1);
int [] varr2 = getVersionArray(v2);
int lim = Math.min(varr1.length, varr2.length);
int k = 0;
while(k < lim) {
if(varr1[k] == varr2[k]) {
k ++;
continue;
}
return varr1[k] > varr2[k] ? 1 : -1;
}
if(varr1.length == varr2.length) {
return 0;
}
return varr1.length > varr2.length ? 1 : -1;
}
public int [] getVersionArray(String v) {
String [] sarray = StringUtils.split(v, ".");
int [] varray = new int[sarray.length];
int k = 0;
while (k < sarray.length) {
varray[k] = Integer.parseInt(sarray[k]);
k ++;
}
return varray;
}
}
结果:
compare("8.2.3", "8.2.3") : 0
compare("8.2.3", "8.7.3") : -1
compare("8.2.3", "8.1.1") : 1
compare("8.2.3", "8.2.15") : -1
compare("8.2.3", "8.2.1.236") : 1
compare("8.2.3", "8.8.1.236") : -1
compare("8.2.3", "9.4") : -1
compare("8.2.3", "6.4") : 1