是否有比较版本号的标准习语?我不能直接使用String compareTo,因为我还不知道点释放的最大数量是多少。我需要比较版本,并有以下保持正确:
1.0 < 1.1
1.0.1 < 1.1
1.9 < 1.10
是否有比较版本号的标准习语?我不能直接使用String compareTo,因为我还不知道点释放的最大数量是多少。我需要比较版本,并有以下保持正确:
1.0 < 1.1
1.0.1 < 1.1
1.9 < 1.10
当前回答
这篇旧文章的另一个解决方案(对那些可能有帮助的人来说):
public class Version implements Comparable<Version> {
private String version;
public final String get() {
return this.version;
}
public Version(String version) {
if(version == null)
throw new IllegalArgumentException("Version can not be null");
if(!version.matches("[0-9]+(\\.[0-9]+)*"))
throw new IllegalArgumentException("Invalid version format");
this.version = version;
}
@Override public int compareTo(Version that) {
if(that == null)
return 1;
String[] thisParts = this.get().split("\\.");
String[] thatParts = that.get().split("\\.");
int length = Math.max(thisParts.length, thatParts.length);
for(int i = 0; i < length; i++) {
int thisPart = i < thisParts.length ?
Integer.parseInt(thisParts[i]) : 0;
int thatPart = i < thatParts.length ?
Integer.parseInt(thatParts[i]) : 0;
if(thisPart < thatPart)
return -1;
if(thisPart > thatPart)
return 1;
}
return 0;
}
@Override public boolean equals(Object that) {
if(this == that)
return true;
if(that == null)
return false;
if(this.getClass() != that.getClass())
return false;
return this.compareTo((Version) that) == 0;
}
}
Version a = new Version("1.1");
Version b = new Version("1.1.1");
a.compareTo(b) // return -1 (a<b)
a.equals(b) // return false
Version a = new Version("2.0");
Version b = new Version("1.9.9");
a.compareTo(b) // return 1 (a>b)
a.equals(b) // return false
Version a = new Version("1.0");
Version b = new Version("1");
a.compareTo(b) // return 0 (a=b)
a.equals(b) // return true
Version a = new Version("1");
Version b = null;
a.compareTo(b) // return 1 (a>b)
a.equals(b) // return false
List<Version> versions = new ArrayList<Version>();
versions.add(new Version("2"));
versions.add(new Version("1.0.5"));
versions.add(new Version("1.01.0"));
versions.add(new Version("1.00.1"));
Collections.min(versions).get() // return min version
Collections.max(versions).get() // return max version
// WARNING
Version a = new Version("2.06");
Version b = new Version("2.060");
a.equals(b) // return false
编辑:
@daiscog:谢谢你的评论,这段代码是为Android平台开发的,由谷歌推荐,方法“匹配”检查整个字符串,不像Java使用监管模式。(Android文档- JAVA文档)
其他回答
// VersionComparator.java
import java.util.Comparator;
public class VersionComparator implements Comparator {
public boolean equals(Object o1, Object o2) {
return compare(o1, o2) == 0;
}
public int compare(Object o1, Object o2) {
String version1 = (String) o1;
String version2 = (String) o2;
VersionTokenizer tokenizer1 = new VersionTokenizer(version1);
VersionTokenizer tokenizer2 = new VersionTokenizer(version2);
int number1 = 0, number2 = 0;
String suffix1 = "", suffix2 = "";
while (tokenizer1.MoveNext()) {
if (!tokenizer2.MoveNext()) {
do {
number1 = tokenizer1.getNumber();
suffix1 = tokenizer1.getSuffix();
if (number1 != 0 || suffix1.length() != 0) {
// Version one is longer than number two, and non-zero
return 1;
}
}
while (tokenizer1.MoveNext());
// Version one is longer than version two, but zero
return 0;
}
number1 = tokenizer1.getNumber();
suffix1 = tokenizer1.getSuffix();
number2 = tokenizer2.getNumber();
suffix2 = tokenizer2.getSuffix();
if (number1 < number2) {
// Number one is less than number two
return -1;
}
if (number1 > number2) {
// Number one is greater than number two
return 1;
}
boolean empty1 = suffix1.length() == 0;
boolean empty2 = suffix2.length() == 0;
if (empty1 && empty2) continue; // No suffixes
if (empty1) return 1; // First suffix is empty (1.2 > 1.2b)
if (empty2) return -1; // Second suffix is empty (1.2a < 1.2)
// Lexical comparison of suffixes
int result = suffix1.compareTo(suffix2);
if (result != 0) return result;
}
if (tokenizer2.MoveNext()) {
do {
number2 = tokenizer2.getNumber();
suffix2 = tokenizer2.getSuffix();
if (number2 != 0 || suffix2.length() != 0) {
// Version one is longer than version two, and non-zero
return -1;
}
}
while (tokenizer2.MoveNext());
// Version two is longer than version one, but zero
return 0;
}
return 0;
}
}
// VersionTokenizer.java
public class VersionTokenizer {
private final String _versionString;
private final int _length;
private int _position;
private int _number;
private String _suffix;
private boolean _hasValue;
public int getNumber() {
return _number;
}
public String getSuffix() {
return _suffix;
}
public boolean hasValue() {
return _hasValue;
}
public VersionTokenizer(String versionString) {
if (versionString == null)
throw new IllegalArgumentException("versionString is null");
_versionString = versionString;
_length = versionString.length();
}
public boolean MoveNext() {
_number = 0;
_suffix = "";
_hasValue = false;
// No more characters
if (_position >= _length)
return false;
_hasValue = true;
while (_position < _length) {
char c = _versionString.charAt(_position);
if (c < '0' || c > '9') break;
_number = _number * 10 + (c - '0');
_position++;
}
int suffixStart = _position;
while (_position < _length) {
char c = _versionString.charAt(_position);
if (c == '.') break;
_position++;
}
_suffix = _versionString.substring(suffixStart, _position);
if (_position < _length) _position++;
return true;
}
}
例子:
public class Main
{
private static VersionComparator cmp;
public static void main (String[] args)
{
cmp = new VersionComparator();
Test(new String[]{"1.1.2", "1.2", "1.2.0", "1.2.1", "1.12"});
Test(new String[]{"1.3", "1.3a", "1.3b", "1.3-SNAPSHOT"});
}
private static void Test(String[] versions) {
for (int i = 0; i < versions.length; i++) {
for (int j = i; j < versions.length; j++) {
Test(versions[i], versions[j]);
}
}
}
private static void Test(String v1, String v2) {
int result = cmp.compare(v1, v2);
String op = "==";
if (result < 0) op = "<";
if (result > 0) op = ">";
System.out.printf("%s %s %s\n", v1, op, v2);
}
}
输出:
1.1.2 == 1.1.2 ---> same length and value
1.1.2 < 1.2 ---> first number (1) less than second number (2) => -1
1.1.2 < 1.2.0 ---> first number (1) less than second number (2) => -1
1.1.2 < 1.2.1 ---> first number (1) less than second number (2) => -1
1.1.2 < 1.12 ---> first number (1) less than second number (12) => -1
1.2 == 1.2 ---> same length and value
1.2 == 1.2.0 ---> first shorter than second, but zero
1.2 < 1.2.1 ---> first shorter than second, and non-zero
1.2 < 1.12 ---> first number (2) less than second number (12) => -1
1.2.0 == 1.2.0 ---> same length and value
1.2.0 < 1.2.1 ---> first number (0) less than second number (1) => -1
1.2.0 < 1.12 ---> first number (2) less than second number (12) => -1
1.2.1 == 1.2.1 ---> same length and value
1.2.1 < 1.12 ---> first number (2) less than second number (12) => -1
1.12 == 1.12 ---> same length and value
1.3 == 1.3 ---> same length and value
1.3 > 1.3a ---> first suffix ('') is empty, but not second ('a') => 1
1.3 > 1.3b ---> first suffix ('') is empty, but not second ('b') => 1
1.3 > 1.3-SNAPSHOT ---> first suffix ('') is empty, but not second ('-SNAPSHOT') => 1
1.3a == 1.3a ---> same length and value
1.3a < 1.3b ---> first suffix ('a') compared to second suffix ('b') => -1
1.3a < 1.3-SNAPSHOT ---> first suffix ('a') compared to second suffix ('-SNAPSHOT') => -1
1.3b == 1.3b ---> same length and value
1.3b < 1.3-SNAPSHOT ---> first suffix ('b') compared to second suffix ('-SNAPSHOT') => -1
1.3-SNAPSHOT == 1.3-SNAPSHOT ---> same length and value
此代码尝试解决这种类型的比较版本。
大多数版本说明符,如>= 1.0,都是不言自明的。的 说明符~>具有特殊含义,最好通过示例来说明。~> 2.0.3是 与>= 2.0.3和< 2.1相同。~> 2.1与>= 2.1相同,且< 3.0.
public static boolean apply(String cmpDeviceVersion, String reqDeviceVersion)
{
Boolean equal = !cmpDeviceVersion.contains(">") && !cmpDeviceVersion.contains(">=") &&
!cmpDeviceVersion.contains("<") && !cmpDeviceVersion.contains("<=") &&
!cmpDeviceVersion.contains("~>");
Boolean between = cmpDeviceVersion.contains("~>");
Boolean higher = cmpDeviceVersion.contains(">") && !cmpDeviceVersion.contains(">=") && !cmpDeviceVersion.contains("~>");
Boolean higherOrEqual = cmpDeviceVersion.contains(">=");
Boolean less = cmpDeviceVersion.contains("<") && !cmpDeviceVersion.contains("<=");
Boolean lessOrEqual = cmpDeviceVersion.contains("<=");
cmpDeviceVersion = cmpDeviceVersion.replaceAll("[<>=~]", "");
cmpDeviceVersion = cmpDeviceVersion.trim();
String[] version = cmpDeviceVersion.split("\\.");
String[] reqVersion = reqDeviceVersion.split("\\.");
if(equal)
{
return isEqual(version, reqVersion);
}
else if(between)
{
return isBetween(version, reqVersion);
}
else if(higher)
{
return isHigher(version, reqVersion);
}
else if(higherOrEqual)
{
return isEqual(version, reqVersion) || isHigher(version, reqVersion);
}
else if(less)
{
return isLess(version, reqVersion);
}
else if(lessOrEqual)
{
return isEqual(version, reqVersion) || isLess(version, reqVersion);
}
return false;
}
private static boolean isEqual(String[] version, String[] reqVersion)
{
String strVersion = StringUtils.join(version);
String strReqVersion = StringUtils.join(reqVersion);
if(version.length > reqVersion.length)
{
Integer diff = version.length - reqVersion.length;
strReqVersion += StringUtils.repeat(".0", diff);
}
else if(reqVersion.length > version.length)
{
Integer diff = reqVersion.length - version.length;
strVersion += StringUtils.repeat(".0", diff);
}
return strVersion.equals(strReqVersion);
}
private static boolean isHigher(String[] version, String[] reqVersion)
{
String strVersion = StringUtils.join(version);
String strReqVersion = StringUtils.join(reqVersion);
if(version.length > reqVersion.length)
{
Integer diff = version.length - reqVersion.length;
strReqVersion += StringUtils.repeat(".0", diff);
}
else if(reqVersion.length > version.length)
{
Integer diff = reqVersion.length - version.length;
strVersion += StringUtils.repeat(".0", diff);
}
return strReqVersion.compareTo(strVersion) > 0;
}
private static boolean isLess(String[] version, String[] reqVersion)
{
String strVersion = StringUtils.join(version);
String strReqVersion = StringUtils.join(reqVersion);
if(version.length > reqVersion.length)
{
Integer diff = version.length - reqVersion.length;
strReqVersion += StringUtils.repeat(".0", diff);
}
else if(reqVersion.length > version.length)
{
Integer diff = reqVersion.length - version.length;
strVersion += StringUtils.repeat(".0", diff);
}
return strReqVersion.compareTo(strVersion) < 0;
}
private static boolean isBetween(String[] version, String[] reqVersion)
{
return (isEqual(version, reqVersion) || isHigher(version, reqVersion)) &&
isLess(getNextVersion(version), reqVersion);
}
private static String[] getNextVersion(String[] version)
{
String[] nextVersion = new String[version.length];
for(int i = version.length - 1; i >= 0 ; i--)
{
if(i == version.length - 1)
{
nextVersion[i] = "0";
}
else if((i == version.length - 2) && NumberUtils.isNumber(version[i]))
{
nextVersion[i] = String.valueOf(NumberUtils.toInt(version[i]) + 1);
}
else
{
nextVersion[i] = version[i];
}
}
return nextVersion;
}
我写了一个名为MgntUtils的开源库,它有一个用于字符串版本的实用程序。它正确地比较它们,适用于版本范围等等。下面是这个库javadoc参见方法TextUtils.comapreVersions(…)它已经被大量使用并经过了良好的测试。下面这篇文章描述了这个库以及如何获取它。它可以作为Maven工件和在github上获得(包括源代码和JavaDoc)
public int CompareVersions(String version1, String version2)
{
String[] string1Vals = version1.split("\\.");
String[] string2Vals = version2.split("\\.");
int length = Math.max(string1Vals.length, string2Vals.length);
for (int i = 0; i < length; i++)
{
Integer v1 = (i < string1Vals.length)?Integer.parseInt(string1Vals[i]):0;
Integer v2 = (i < string2Vals.length)?Integer.parseInt(string2Vals[i]):0;
//Making sure Version1 bigger than version2
if (v1 > v2)
{
return 1;
}
//Making sure Version1 smaller than version2
else if(v1 < v2)
{
return -1;
}
}
//Both are equal
return 0;
}
使用Maven真的很简单:
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
DefaultArtifactVersion minVersion = new DefaultArtifactVersion("1.0.1");
DefaultArtifactVersion maxVersion = new DefaultArtifactVersion("1.10");
DefaultArtifactVersion version = new DefaultArtifactVersion("1.11");
if (version.compareTo(minVersion) < 0 || version.compareTo(maxVersion) > 0) {
System.out.println("Sorry, your version is unsupported");
}
您可以从这个页面获得Maven Artifact的正确依赖项字符串:
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<version>3.0.3</version>
</dependency>