是否有比较版本号的标准习惯用法?我不能只使用直接的字符串比较,因为我还不知道最大点数发布的数量.我需要比较版本,并具有以下成立:
1.0 < 1.1 1.0.1 < 1.1 1.9 < 1.10
alex.. 168
这个旧帖子的另一个解决方案(对于那些可能有帮助的人):
public class Version implements Comparable{ 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 (ab) 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 Listversions = new ArrayList (); 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平台开发的,并且根据Google的推荐,"匹配"方法检查整个字符串,而不像使用监管模式的Java.(Android文档 - JAVA文档)
这个旧帖子的另一个解决方案(对于那些可能有帮助的人):
public class Version implements Comparable{ 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 (ab) 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 Listversions = new ArrayList (); 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平台开发的,并且根据Google的推荐,"匹配"方法检查整个字符串,而不像使用监管模式的Java.(Android文档 - JAVA文档)
使用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的正确依赖关系字符串:
org.apache.maven maven-artifact 3.0.3
使用点作为分隔符对字符串进行标记,然后从左侧开始并排比较整数平移.
您需要规范化版本字符串,以便对它们进行比较.就像是
import java.util.regex.Pattern; public class Main { public static void main(String... args) { compare("1.0", "1.1"); compare("1.0.1", "1.1"); compare("1.9", "1.10"); compare("1.a", "1.9"); } private static void compare(String v1, String v2) { String s1 = normalisedVersion(v1); String s2 = normalisedVersion(v2); int cmp = s1.compareTo(s2); String cmpStr = cmp < 0 ? "<" : cmp > 0 ? ">" : "=="; System.out.printf("'%s' %s '%s'%n", v1, cmpStr, v2); } public static String normalisedVersion(String version) { return normalisedVersion(version, ".", 4); } public static String normalisedVersion(String version, String sep, int maxWidth) { String[] split = Pattern.compile(sep, Pattern.LITERAL).split(version); StringBuilder sb = new StringBuilder(); for (String s : split) { sb.append(String.format("%" + maxWidth + 's', s)); } return sb.toString(); } }
打印
'1.0' < '1.1' '1.0.1' < '1.1' '1.9' < '1.10' '1.a' > '1.9'
最好重用现有代码,使用Maven的ComparableVersion类
好处:
Apache许可证,版本2.0,
测试,
在spring-security-core,jboss等多个项目中使用(复制)
多种功能
它已经是java.lang.Comparable了
只复制粘贴一个类,没有第三方依赖
不要包含对maven-artifact的依赖,因为这会引发各种传递依赖
// 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
想知道为什么每个人都认为这些版本只是由整数组成 - 在我的例子中并非如此.
为什么重新发明轮子(假设版本遵循Semver标准)
首先通过Maven 安装https://github.com/vdurmont/semver4j
然后使用这个库
Semver sem = new Semver("1.2.3"); sem.isGreaterThan("1.2.2"); // true
public static int compareVersions(String version1, String version2){ String[] levels1 = version1.split("\\."); String[] levels2 = version2.split("\\."); int length = Math.max(levels1.length, levels2.length); for (int i = 0; i < length; i++){ Integer v1 = i < levels1.length ? Integer.parseInt(levels1[i]) : 0; Integer v2 = i < levels2.length ? Integer.parseInt(levels2[i]) : 0; int compare = v1.compareTo(v2); if (compare != 0){ return compare; } } return 0; }