¿Cómo se comparan dos cadenas de versiones en Java?

¿Hay una expresión estándar para comparar números de versión? No puedo usar una comparación directa de cadenas porque todavía no sé cuál será el número máximo de lanzamientos de puntos. Necesito comparar las versiones y hacer que lo siguiente sea verdadero:

1.0 < 1.1 1.0.1 < 1.1 1.9 < 1.10 

Tokenice las cadenas con el punto como delimitador y luego compare la traducción de entero al lado del otro, comenzando desde la izquierda.

Otra solución para esta publicación anterior (para aquellos que podría ayudar):

 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 List versions = 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 

Editar:

@daiscog: Gracias por su comentario, este código ha sido desarrollado para la plataforma Android y, como recomienda Google, el método “coincide” comprueba toda la cadena a diferencia de Java que usa un patrón reglamentario. ( Documentación de Android – documentación de JAVA )

Es muy fácil usar 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"); } 

Puede obtener la cadena de dependencia correcta para Maven Artifact desde esta página :

  org.apache.maven maven-artifact 3.0.3  

Necesita normalizar las cadenas de versión para que puedan ser comparadas. Algo como

 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(); } } 

Huellas dactilares

 '1.0' < '1.1' '1.0.1' < '1.1' '1.9' < '1.10' '1.a' > '1.9' 

Lo mejor para reutilizar el código existente, tome la clase ComparableVersion de Maven

ventajas:

  • Licencia Apache, Versión 2.0,
  • probado,
  • usado (copiado) en proyectos múltiples como spring-security-core, jboss, etc.
  • múltiples funciones
  • ya es un java.lang.Comparable
  • simplemente copie y pegue esa clase, no dependencias de terceros

No incluya la dependencia a maven-artefact ya que eso atraerá varias dependencias transitivas

 // 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; } } 

Ejemplo:

 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); } } 

Salida:

 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 

Me pregunto por qué todos asumen que las versiones solo están compuestas por números enteros, en mi caso no fue así.

Por qué reinventar la rueda (suponiendo que la versión sigue el estándar Semver)

Primero instala https://github.com/vdurmont/semver4j a través de Maven

Entonces usa esta biblioteca

 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; } 
 public int compare(String v1, String v2) { v1 = v1.replaceAll("\\s", ""); v2 = v2.replaceAll("\\s", ""); String[] a1 = v1.split("\\."); String[] a2 = v2.split("\\."); List l1 = Arrays.asList(a1); List l2 = Arrays.asList(a2); int i=0; while(true){ Double d1 = null; Double d2 = null; try{ d1 = Double.parseDouble(l1.get(i)); }catch(IndexOutOfBoundsException e){ } try{ d2 = Double.parseDouble(l2.get(i)); }catch(IndexOutOfBoundsException e){ } if (d1 != null && d2 != null) { if (d1.doubleValue() > d2.doubleValue()) { return 1; } else if (d1.doubleValue() < d2.doubleValue()) { return -1; } } else if (d2 == null && d1 != null) { if (d1.doubleValue() > 0) { return 1; } } else if (d1 == null && d2 != null) { if (d2.doubleValue() > 0) { return -1; } } else { break; } i++; } return 0; } 

para mis proyectos uso mi biblioteca commons-version https://github.com/raydac/commons-version contiene dos clases auxiliares – para analizar la versión (la versión analizada se puede comparar con otra versión porque es comparable) y VersionValidator que permite verificar la versión de alguna expresión como !=ide-1.1.1,>idea-1.3.4-SNAPSHOT;<1.2.3

Creé una utilidad simple para comparar versiones en la plataforma Android utilizando la convención de versiones semánticas . Por lo tanto, funciona solo para cadenas en formato XYZ (Major.Minor.Patch) donde X, Y y Z son enteros no negativos. Puedes encontrarlo en mi GitHub .

Método Version.compareVersions (String v1, String v2) compara dos cadenas de versión. Devuelve 0 si las versiones son iguales, 1 si la versión v1 es anterior a la versión v2, -1 si la versión v1 es posterior a la versión v2, -2 si el formato de la versión no es válido.

 /** * written by: Stan Towianski - May 2018 * notes: I make assumption each of 3 version sections abc is not longer then 4 digits: aaaa.bbbb.cccc-MODWORD1(-)modnum2 * 5.10.13-release-1 becomes 0000500100013.501 6.0-snapshot becomes 0000600000000.100 * MODWORD1 = -xyz/NotMatching, -SNAPSHOT, -ALPHA, -BETA, -RC, -RELEASE/nothing return: .0, .1, .2, .3, .4, .5 * modnum2 = up to 2 digit/chars second version * */ public class VersionCk { private static boolean isVersionHigher( String baseVersion, String testVersion ) { System.out.println( "versionToComparable( baseVersion ) =" + versionToComparable( baseVersion ) ); System.out.println( "versionToComparable( testVersion ) =" + versionToComparable( testVersion ) + " is this higher ?" ); return versionToComparable( testVersion ).compareTo( versionToComparable( baseVersion ) ) > 0; } //---- not worrying about += for something so small private static String versionToComparable( String version ) { // System.out.println("version - " + version); String versionNum = version; int at = version.indexOf( '-' ); if ( at >= 0 ) versionNum = version.substring( 0, at ); String[] numAr = versionNum.split( "\\." ); String versionFormatted = "0"; for ( String tmp : numAr ) { versionFormatted += String.format( "%4s", tmp ).replace(' ', '0'); } while ( versionFormatted.length() < 12 ) // pad out to aaaa.bbbb.cccc { versionFormatted += "0000"; } // System.out.println( "converted min version =" + versionFormatted + "= : " + versionNum ); return versionFormatted + getVersionModifier( version, at ); } //---- use order low to high: -xyz, -SNAPSHOT, -ALPHA, -BETA, -RC, -RELEASE/nothing returns: 0, 1, 2, 3, 4, 5 private static String getVersionModifier( String version, int at ) { // System.out.println("version - " + version ); String[] wordModsAr = { "-SNAPSHOT", "-ALPHA", "-BETA", "-RC", "-RELEASE" }; if ( at < 0 ) return "." + wordModsAr.length + "00"; // make nothing = RELEASE level int i = 1; for ( String word : wordModsAr ) { if ( ( at = version.toUpperCase().indexOf( word ) ) > 0 ) return "." + i + getSecondVersionModifier( version.substring( at + word.length() ) ); i++; } return ".000"; } //---- add 2 chars for any number after first modifier. -rc2 or -rc-2 returns 02 private static String getSecondVersionModifier( String version ) { System.out.println( "second modifier =" + version + "=" ); Matcher m = Pattern.compile("(.*?)(\\d+).*").matcher( version ); // if ( m.matches() ) // System.out.println( "match ? =" + m.matches() + "= m.group(1) =" + m.group(1) + "= m.group(2) =" + m.group(2) + "= m.group(3) =" + (m.groupCount() >= 3 ? m.group(3) : "x") ); // else // System.out.println( "No match" ); return m.matches() ? String.format( "%2s", m.group(2) ).replace(' ', '0') : "00"; } public static void main(String[] args) { checkVersion( "3.10.0", "3.4.0"); checkVersion( "5.4.2", "5.4.1"); checkVersion( "5.4.4", "5.4.5"); checkVersion( "5.4.9", "5.4.12"); checkVersion( "5.9.222", "5.10.12"); checkVersion( "5.10.12", "5.10.12"); checkVersion( "5.10.13", "5.10.14"); checkVersion( "6.7.0", "6.8"); checkVersion( "6.7", "2.7.0"); checkVersion( "6", "6.3.1"); checkVersion( "4", "4.0.0"); checkVersion( "6.3.0", "6"); checkVersion( "5.10.12-Alpha", "5.10.12-beTA"); checkVersion( "5.10.13-release", "5.10.14-beta"); checkVersion( "6.7.0", "6.8-snapshot"); checkVersion( "6.7.1", "6.7.0-release"); checkVersion( "6-snapshot", "6.0.0-beta"); checkVersion( "6.0-snapshot", "6.0.0-whatthe"); checkVersion( "5.10.12-Alpha-1", "5.10.12-alpha-2"); checkVersion( "5.10.13-release-1", "5.10.13-release2"); checkVersion( "10-rc42", "10.0.0-rc53"); } private static void checkVersion(String baseVersion, String testVersion) { System.out.println( "baseVersion - " + baseVersion ); System.out.println( "testVersion - " + testVersion ); System.out.println( "isVersionHigher = " + isVersionHigher( baseVersion, testVersion ) ); System.out.println( "---------------"); } } 

alguna salida:

 --------------- baseVersion - 6.7 testVersion - 2.7.0 versionToComparable( baseVersion ) =0000600070000.500 versionToComparable( testVersion ) =0000200070000.500 is this higher ? isVersionHigher = false --------------- baseVersion - 6 testVersion - 6.3.1 versionToComparable( baseVersion ) =0000600000000.500 versionToComparable( testVersion ) =0000600030001.500 is this higher ? isVersionHigher = true --------------- baseVersion - 4 testVersion - 4.0.0 versionToComparable( baseVersion ) =0000400000000.500 versionToComparable( testVersion ) =0000400000000.500 is this higher ? isVersionHigher = false --------------- baseVersion - 6.3.0 testVersion - 6 versionToComparable( baseVersion ) =0000600030000.500 versionToComparable( testVersion ) =0000600000000.500 is this higher ? isVersionHigher = false --------------- baseVersion - 5.10.12-Alpha testVersion - 5.10.12-beTA second modifier == versionToComparable( baseVersion ) =0000500100012.200 second modifier == versionToComparable( testVersion ) =0000500100012.300 is this higher ? second modifier == second modifier == isVersionHigher = true --------------- baseVersion - 5.10.13-release testVersion - 5.10.14-beta second modifier == versionToComparable( baseVersion ) =0000500100013.500 second modifier == versionToComparable( testVersion ) =0000500100014.300 is this higher ? second modifier == second modifier == isVersionHigher = true --------------- baseVersion - 6.7.0 testVersion - 6.8-snapshot versionToComparable( baseVersion ) =0000600070000.500 second modifier == versionToComparable( testVersion ) =0000600080000.100 is this higher ? second modifier == isVersionHigher = true --------------- baseVersion - 6.7.1 testVersion - 6.7.0-release versionToComparable( baseVersion ) =0000600070001.500 second modifier == versionToComparable( testVersion ) =0000600070000.500 is this higher ? second modifier == isVersionHigher = false --------------- baseVersion - 6-snapshot testVersion - 6.0.0-beta second modifier == versionToComparable( baseVersion ) =0000600000000.100 second modifier == versionToComparable( testVersion ) =0000600000000.300 is this higher ? second modifier == second modifier == isVersionHigher = true --------------- baseVersion - 6.0-snapshot testVersion - 6.0.0-whatthe second modifier == versionToComparable( baseVersion ) =0000600000000.100 versionToComparable( testVersion ) =0000600000000.000 is this higher ? second modifier == isVersionHigher = false --------------- baseVersion - 5.10.12-Alpha-1 testVersion - 5.10.12-alpha-2 second modifier =-1= versionToComparable( baseVersion ) =0000500100012.201 second modifier =-2= versionToComparable( testVersion ) =0000500100012.202 is this higher ? second modifier =-2= second modifier =-1= isVersionHigher = true --------------- baseVersion - 5.10.13-release-1 testVersion - 5.10.13-release2 second modifier =-1= versionToComparable( baseVersion ) =0000500100013.501 second modifier =2= versionToComparable( testVersion ) =0000500100013.502 is this higher ? second modifier =2= second modifier =-1= isVersionHigher = true --------------- baseVersion - 10-rc42 testVersion - 10.0.0-rc53 second modifier =42= versionToComparable( baseVersion ) =0001000000000.442 second modifier =53= versionToComparable( testVersion ) =0001000000000.453 is this higher ? second modifier =53= second modifier =42= isVersionHigher = true --------------- 

Escribí una pequeña función por mi cuenta. Simplificar el uso de listas

  public static boolean checkVersionUpdate(String olderVerison, String newVersion) { if (olderVerison.length() == 0 || newVersion.length() == 0) { return false; } List newVerList = Arrays.asList(newVersion.split("\\.")); List oldVerList = Arrays.asList(olderVerison.split("\\.")); int diff = newVerList.size() - oldVerList.size(); List newList = new ArrayList<>(); if (diff > 0) { newList.addAll(oldVerList); for (int i = 0; i < diff; i++) { newList.add("0"); } return examineArray(newList, newVerList, diff); } else if (diff < 0) { newList.addAll(newVerList); for (int i = 0; i < -diff; i++) { newList.add("0"); } return examineArray(oldVerList, newList, diff); } else { return examineArray(oldVerList, newVerList, diff); } } public static boolean examineArray(List oldList, List newList, int diff) { boolean newVersionGreater = false; for (int i = 0; i < oldList.size(); i++) { if (Integer.parseInt(newList.get(i)) > Integer.parseInt(oldList.get(i))) { newVersionGreater = true; break; } else if (Integer.parseInt(newList.get(i)) < Integer.parseInt(oldList.get(i))) { newVersionGreater = false; break; } else { newVersionGreater = diff > 0; } } return newVersionGreater; } 

Para Scala puede usar una biblioteca que hice: https://github.com/kypeli/sversion

 Version("1.2") > Version("1.1") // true Version("1.2.1") > Version("1.1.2") // true Version("1.1.1") == Version("1.1.1") // true Version("1.1.1") > Version("1.1") // true Version("1.1.0") == Version("1.1") // true Version("1.1-RC2") > Version("1.1-RC1") // true Version("1.1-RC1") > Version("1.1") // true 

Este código intenta resolver este tipo de versiones de comparación.

La mayoría de los especificadores de versión, como> = 1.0, se explican por sí mismos. El especificador ~> tiene un significado especial, que se muestra mejor con el ejemplo. ~> 2.0.3 es idéntico a> = 2.0.3 y <2.1. ~> 2.1 es idéntico a> = 2.1 y <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; } 

Me gustó la idea de @Peter Lawrey, y la extendí a otros límites:

  /** * Normalize string array, * Appends zeros if string from the array * has length smaller than the maxLen. **/ private String normalize(String[] split, int maxLen){ StringBuilder sb = new StringBuilder(""); for(String s : split) { for(int i = 0; i= 0 && (s.charAt(i) == '.' || s.charAt(i) == '0')){ if(s.charAt(i) == '.') k = i-1; i--; } return s.substring(0,k+1); } /** * Compares two versions(works for alphabets too), * Returns 1 if v1 > v2, returns 0 if v1 == v2, * and returns -1 if v1 < v2. **/ public int compareVersion(String v1, String v2) { // Uncomment below two lines if for you, say, 4.1.0 is equal to 4.1 // v1 = removeTrailingZeros(v1); // v2 = removeTrailingZeros(v2); String[] splitv1 = v1.split("\\."); String[] splitv2 = v2.split("\\."); int maxLen = 0; for(String str : splitv1) maxLen = Math.max(maxLen, str.length()); for(String str : splitv2) maxLen = Math.max(maxLen, str.length()); int cmp = normalize(splitv1, maxLen).compareTo(normalize(splitv2, maxLen)); return cmp > 0 ? 1 : (cmp < 0 ? -1 : 0); } 

Espero que ayude a alguien. Pasó todos los casos de prueba en interviewbit y leetcode (necesita descomentar dos líneas en la función compareVersion).

¡Fácil de probar!

Seamos honestos, String comparación lexicográfica de String funcionará bastante bien para la mayoría del caso.

 "1.0.0".compareTo("1.0.1") // -1 

Existe algún error posible, si, por ejemplo, tenemos algún formato en alguna versión

 "1.01.1".compareTo("1.1.0") // -1 should be 1 

Esto se puede corregir fácilmente limpiando esos valores y, por supuesto, proporcionando algunos métodos simples.

 public class Version implements Comparable{ private static final String sanitizeRegex = ".0([1-9]+)"; private String value; public Version(String value){ if(value == null) throw new IllegalArgumentException("A version need a valid value"); this.value = value.replaceAll(sanitizeRegex, ".$1"); } public boolean isLessThan(Version o){ return this.compareTo(o) < 0; } public boolean isGreaterThan(Version o){ return this.compareTo(o) > 0; } public boolean isEquals(Version o) { return this.compareTo(o) == 0; } @Override public int compareTo(Version o) { return this.value.compareTo(o.value); } @Override public String toString() { return value; } } 

The regex will remove remove the leading 0 if needed.

 01 -> 1 0 -> 0 00 -> 0 10 -> 10 

This allows us to have a pretty simple comparison using what existe already.

A quick test case taken from Johan Paul’s answer

  System.out.println(new Version("1.2").isGreaterThan(new Version("1.1") )); // true System.out.println(new Version("1.2.1").isGreaterThan(new Version("1.1.2") )); // true System.out.println(new Version("1.1.1").isGreaterThan(new Version("1.1.1") )); // false System.out.println(new Version("1.1.1").isGreaterThan(new Version("1.1") )); // true System.out.println(new Version("1.1.0").isGreaterThan(new Version("1.1") )); // true System.out.println(new Version("1.1-RC2").isGreaterThan(new Version("1.1-RC1"))); // true System.out.println(new Version("1.1-RC1").isGreaterThan(new Version("1.1") )); // true System.out.println(new Version("1.0.1").isGreaterThan(new Version("1.1") )); // false 
 public class VersionComparator { /* loop through both version strings * then loop through the inner string to computer the val of the int * for each integer read, do num*10+ * and stop when stumbling upon '.' * When '.' is encountered... * see if '.' is encountered for both strings * if it is then compare num1 and num2 * if num1 == num2... iterate over p1++, p2++ * else return (num1 > num2) ? 1 : -1 * If both the string end then compare(num1, num2) return 0, 1, -1 * else loop through the longer string and * verify if it only has trailing zeros * If it only has trailing zeros then return 0 * else it is greater than the other string */ public static int compareVersions(String v1, String v2) { int num1 = 0; int num2 = 0; int p1 = 0; int p2 = 0; while (p1 < v1.length() && p2 < v2.length()) { num1 = Integer.parseInt(v1.charAt(p1) + ""); num2 = Integer.parseInt(v2.charAt(p2) + ""); p1++; p2++; while (p1 < v1.length() && p2 < v2.length() && v1.charAt(p1) != '.' && v2.charAt(p2) != '.') { if (p1 < v1.length()) num1 = num1 * 10 + Integer.parseInt(v1.charAt(p1) + ""); if (p2 < v2.length()) num2 = num2 * 10 + Integer.parseInt(v2.charAt(p2) + ""); p1++; p2++; } if (p1 < v1.length() && p2 < v2.length() && v1.charAt(p1) == '.' && v2.charAt(p2) == '.') { if ((num1 ^ num2) == 0) { p1++; p2++; } else return (num1 > num2) ? 1 : -1; } else if (p1 < v1.length() && p2 < v2.length() && v1.charAt(p1) == '.') return -1; else if (p1 < v1.length() && p2 < v2.length() && v2.charAt(p2) == '.') return 1; } if (p1 == v1.length() && p2 == v2.length()) { if ((num1 ^ num2) == 0) return 0; else return (num1 > num2) ? 1 : -1; } else if (p1 == v1.length()) { if ((num1 ^ num2) == 0) { while (p2 < v2.length()) { if (v2.charAt(p2) != '.' && v2.charAt(p2) != '0') return -1; p2++; } return 0; } else return (num1 > num2) ? 1 : -1; } else { if ((num1 ^ num2) == 0) { while (p1 < v1.length()) { if (v1.charAt(p1) != '.' && v1.charAt(p1) != '0') return 1; p1++; } return 0; } else return (num1 > num2) ? 1 : -1; } } public static void main(String[] args) { System.out.println(compareVersions("11.23", "11.21.1.0.0.1.0") ^ 1); System.out.println(compareVersions("11.21.1.0.0.1.0", "11.23") ^ -1); System.out.println(compareVersions("11.23", "11.23.0.0.0.1.0") ^ -1); System.out.println(compareVersions("11.2", "11.23") ^ -1); System.out.println(compareVersions("11.23", "11.21.1.0.0.1.0") ^ 1); System.out.println(compareVersions("1.21.1.0.0.1.0", "2.23") ^ -1); System.out.println(compareVersions("11.23", "11.21.1.0.0.1.0") ^ 1); System.out.println(compareVersions("11.23.0.0.0.0.0", "11.23") ^ 0); System.out.println(compareVersions("11.23", "11.21.1.0.0.1.0") ^ 1); System.out.println(compareVersions("1.5.1.3", "1.5.1.3.0") ^ 0); System.out.println(compareVersions("1.5.1.4", "1.5.1.3.0") ^ 1); System.out.println(compareVersions("1.2.1.3", "1.5.1.3.0") ^ -1); System.out.println(compareVersions("1.2.1.3", "1.22.1.3.0") ^ -1); System.out.println(compareVersions("1.222.1.3", "1.22.1.3.0") ^ 1); } } 

Here’s an optimized implementation:

 public static final Comparator VERSION_ORDER = new Comparator() { @Override public int compare (CharSequence lhs, CharSequence rhs) { int ll = lhs.length(), rl = rhs.length(), lv = 0, rv = 0, li = 0, ri = 0; char c; do { lv = rv = 0; while (--ll >= 0) { c = lhs.charAt(li++); if (c < '0' || c > '9') break; lv = lv*10 + c - '0'; } while (--rl >= 0) { c = rhs.charAt(ri++); if (c < '0' || c > '9') break; rv = rv*10 + c - '0'; } } while (lv == rv && (ll >= 0 || rl >= 0)); return lv - rv; } }; 

Resultado:

 "0.1" - "1.0" = -1 "1.0" - "1.0" = 0 "1.0" - "1.0.0" = 0 "10" - "1.0" = 9 "3.7.6" - "3.7.11" = -5 "foobar" - "1.0" = -1 

My java Solution

  public int compareVersion(String version1, String version2) { String[] first = version1.split("\\."); String[] second = version2.split("\\."); int len = first.length<=second.length? first.length:second.length; // the loop runs for whichever is the short version of two strings for(int i=0;isecondInt){ return 1; } } // below two condition check if the length are the not the same //if first string length is short then start from after first string length and compare it with second string value. second string value is not zero that means it is greater. if(first.lengthsecond.length){ for(int i=second.length;i