How do you compare two version Strings in Java?

JavaComparisonVersioning

Java Problem Overview


Is there a standard idiom for comparing version numbers? I can't just use a straight String compareTo because I don't know yet what the maximum number of point releases there will be. I need to compare the versions and have the following hold true:

1.0 < 1.1
1.0.1 < 1.1
1.9 < 1.10

Java Solutions


Solution 1 - Java

Another solution for this old post (for those that it might help) :

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

Edit:

@daiscog: Thank you for your remark, this piece of code has been developed for the Android platform and as recommended by Google, the method "matches" check the entire string unlike Java that uses a regulatory pattern. (Android documentation - JAVA documentation)

Solution 2 - Java

It's really easy using 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");
}

You can get the right dependency string for Maven Artifact from this page:

<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<version>3.0.3</version>
</dependency>

Solution 3 - Java

Tokenize the strings with the dot as delimiter and then compare the integer translation side by side, beginning from the left.

Solution 4 - Java

You need to normalise the version strings so they can be compared. Something like

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

Prints

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

Solution 5 - Java

The best to reuse existing code, take Maven's ComparableVersion class

advantages:

  • Apache License, Version 2.0,
  • tested,
  • used (copied) in multiple projects like spring-security-core, jboss etc
  • multiple features
  • it's already a java.lang.Comparable
  • just copy-paste that one class, no third-party dependencies

Don't include dependency to maven-artifact as that will pull various transitive dependencies

Solution 6 - 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;
    }
}

Example:

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

Output:

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

Solution 7 - Java

Use Java 9's own builtin Version class

import java.util.*;
import java.lang.module.ModuleDescriptor.Version;
class Main {
  public static void main(String[] args) {
    var versions = Arrays.asList(
      "1.0.2",
      "1.0.0-beta.2",
      "1.0.0",
      "1.0.0-beta",
      "1.0.0-alpha.12",
      "1.0.0-beta.11",
      "1.0.1",
      "1.0.11",
      "1.0.0-rc.1",
      "1.0.0-alpha.1",
      "1.1.0",
      "1.0.0-alpha.beta",
      "1.11.0",
      "1.0.0-alpha.12.ab-c",
      "0.0.1",
      "1.2.1",
      "1.0.0-alpha",
      "1.0.0.1",  // Also works with a number of sections different than 3
      "1.0.0.2",
      "2",
      "10",
      "1.0.0.10"
    );
    versions.stream()
      .map(Version::parse)
      .sorted()
      .forEach(System.out::println);
  }
}

[Try it online!][TIO-ks65hpf0]

[TIO-ks65hpf0]: https://tio.run/##dZI9b8JADIb3/AqLKVTlyNEN1AGp3cqE1KXq4CQXcnC5i@xLEKr47WkaUAkBJsuvH3/I9hZrnGzTXdPoonTkYdsKovLaiKdF0NcM2o0oXFoZJVadeVOckC69I/GpiLWziyAxyAwr1BZ@AoCyio1OgD361tROp1C0sXDtSdvN1zcgbXjcoQA1EtSnQgyvsCTCAwvkD80@7AiAkRSRmI2e@240iZXHG/UeNNTQlDkKeb@glNfy0JXDLEqEfNChL8vb2U7UcEL5kJQzgfEkuQSj4XyzB6MMxD8KYDqFpWEHe0c7hr32OSDYqogVgcuAVeK7o6Q6yxQp68HnaOHlulRvi/2FRjcto1EnjBens59PLtiTwiIcn2lRYBmeH2s@L5FY/Ye4fUuVXtDM0Tsmebg@sFeFcJVvM9oX88Z2XY7BsWl@AQ "Java (JDK) – Try It Online"

Output:

0.0.1
1.0.0-alpha
1.0.0-alpha.1
1.0.0-alpha.12
1.0.0-alpha.12.ab-c
1.0.0-alpha.beta
1.0.0-beta
1.0.0-beta.2
1.0.0-beta.11
1.0.0-rc.1
1.0.0
1.0.0.1
1.0.0.2
1.0.0.10
1.0.1
1.0.2
1.0.11
1.1.0
1.2.1
1.11.0
2
10

Solution 8 - Java

Wondering why everybody assumes that the versions is only made up of integers - in my case it was not.

Why reinventing the wheel (assuming the version follows the Semver standard)

First install https://github.com/vdurmont/semver4j via Maven

Then use this library

Semver sem = new Semver("1.2.3");
sem.isGreaterThan("1.2.2"); // true

Solution 9 - Java

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

Solution 10 - Java

If you already have Jackson in your project, you can use com.fasterxml.jackson.core.Version:

import com.fasterxml.jackson.core.Version;
import org.junit.Test;

import static org.junit.Assert.assertTrue;

public class VersionTest {

    @Test
    public void shouldCompareVersion() {
        Version version1 = new Version(1, 11, 1, null, null, null);
        Version version2 = new Version(1, 12, 1, null, null, null);
        assertTrue(version1.compareTo(version2) < 0);
    }
}

Solution 11 - Java

@alex's post on Kotlin

class Version(inputVersion: String) : Comparable<Version> {
    
        var version: String
            private set
    
        override fun compareTo(other: Version) =
            (split() to other.split()).let {(thisParts, thatParts)->
                val length = max(thisParts.size, thatParts.size)
                for (i in 0 until length) {
                    val thisPart = if (i < thisParts.size) thisParts[i].toInt() else 0
                    val thatPart = if (i < thatParts.size) thatParts[i].toInt() else 0
                    if (thisPart < thatPart) return -1
                    if (thisPart > thatPart) return 1
                }
                 0
            }
    
        init {
            require(inputVersion.matches("[0-9]+(\\.[0-9]+)*".toRegex())) { "Invalid version format" }
            version = inputVersion
        }
    }
    
    fun Version.split() = version.split(".").toTypedArray()

usage:

Version("1.2.4").compareTo(Version("0.0.5")) //return 1

Solution 12 - Java

public int compare(String v1, String v2) {
		v1 = v1.replaceAll("\\s", "");
		v2 = v2.replaceAll("\\s", "");
		String[] a1 = v1.split("\\.");
		String[] a2 = v2.split("\\.");
		List<String> l1 = Arrays.asList(a1);
		List<String> 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;
	}

Solution 13 - Java

/**  
 *  written by: Stan Towianski - May 2018 
 * notes: I make assumption each of 3 version sections a.b.c 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( "---------------");
        }
    
    }

some output:

---------------
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
---------------

Solution 14 - Java

I wrote an Open Source Library called MgntUtils that has a utility that works with String versions. It correctly compares them, works with version ranges and so forth. Here is this library javadoc See methods TextUtils.comapreVersions(...). It has been heavily used and well tested. Here is the article that describes the library and where to get it. It is available as Maven artifact and on the github (with sources and JavaDoc)

Solution 15 - Java

I done it right now and asked myself, is it correct? Because I've never found a cleanest solution before than mine:

You just need to split the string versions ("1.0.0") like this example:

userVersion.split("\\.")

Then you will have: {"1", "0", "0"}

Now, using the method that I done:

isUpdateAvailable(userVersion.split("\\."), latestVersionSplit.split("\\."));

Method:

/**
 * Compare two versions
 *
 * @param userVersionSplit   - User string array with major, minor and patch version from user (exemple: {"5", "2", "70"})
 * @param latestVersionSplit - Latest string array with major, minor and patch version from api (example: {"5", "2", "71"})
 * @return true if user version is smaller than latest version
 */
public static boolean isUpdateAvailable(String[] userVersionSplit, String[] latestVersionSplit) {

    try {
        int majorUserVersion = Integer.parseInt(userVersionSplit[0]);
        int minorUserVersion = Integer.parseInt(userVersionSplit[1]);
        int patchUserVersion = Integer.parseInt(userVersionSplit[2]);

        int majorLatestVersion = Integer.parseInt(latestVersionSplit[0]);
        int minorLatestVersion = Integer.parseInt(latestVersionSplit[1]);
        int patchLatestVersion = Integer.parseInt(latestVersionSplit[2]);

        if (majorUserVersion <= majorLatestVersion) {
            if (majorUserVersion < majorLatestVersion) {
                return true;
            } else {
                if (minorUserVersion <= minorLatestVersion) {
                    if (minorUserVersion < minorLatestVersion) {
                        return true;
                    } else {
                        return patchUserVersion < patchLatestVersion;
                    }
                }
            }
        }
    } catch (Exception ignored) {
        // Will be throw only if the versions pattern is different from "x.x.x" format
        // Will return false at the end
    }

    return false;
}

Waiting for any feedback :)

Solution 16 - Java

I created simple utility for comparing versions on Android platform using Semantic Versioning convention. So it works only for strings in format X.Y.Z (Major.Minor.Patch) where X, Y, and Z are non-negative integers. You can find it on my GitHub.

Method Version.compareVersions(String v1, String v2) compares two version strings. It returns 0 if the versions are equal, 1 if version v1 is before version v2, -1 if version v1 is after version v2, -2 if version format is invalid.

Solution 17 - Java

for my projects I use my commons-version library https://github.com/raydac/commons-version it contains two auxiliary classes - to parse version (parsed version can be compared with another version object because it is comparable one) and VersionValidator which allows to check version for some expression like !=ide-1.1.1,>idea-1.3.4-SNAPSHOT;<1.2.3

Solution 18 - Java

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

Solution 19 - Java

I have written a small Java/Android library for comparing version numbers: https://github.com/G00fY2/version-compare

What it basically does is this:

  public int compareVersions(String versionA, String versionB) {
    String[] versionTokensA = versionA.split("\\.");
    String[] versionTokensB = versionB.split("\\.");
    List<Integer> versionNumbersA = new ArrayList<>();
    List<Integer> versionNumbersB = new ArrayList<>();

    for (String versionToken : versionTokensA) {
      versionNumbersA.add(Integer.parseInt(versionToken));
    }
    for (String versionToken : versionTokensB) {
      versionNumbersB.add(Integer.parseInt(versionToken));
    }

    final int versionASize = versionNumbersA.size();
    final int versionBSize = versionNumbersB.size();
    int maxSize = Math.max(versionASize, versionBSize);

    for (int i = 0; i < maxSize; i++) {
      if ((i < versionASize ? versionNumbersA.get(i) : 0) > (i < versionBSize ? versionNumbersB.get(i) : 0)) {
        return 1;
      } else if ((i < versionASize ? versionNumbersA.get(i) : 0) < (i < versionBSize ? versionNumbersB.get(i) : 0)) {
        return -1;
      }
    }
    return 0;
  }

This snippet doesn't offer any error checks or handling. Beside that my library also supports suffixes like "1.2-rc" > "1.2-beta".

Solution 20 - Java

Since no answer on this page handles mixed text well, I made my own version:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

class Main {
    static double parseVersion(String v) {
        if (v.isEmpty()) {
            return 0;
        }
        Pattern p = Pattern.compile("^(\\D*)(\\d*)(\\D*)$");
        Matcher m = p.matcher(v);
        m.find();
        if (m.group(2).isEmpty()) {
            // v1.0.0.[preview]
            return -1;
        }
        double i = Integer.parseInt(m.group(2));
        if (!m.group(3).isEmpty()) {
            // v1.0.[0b]
            i -= 0.1;
        }
        return i;
    }

    public static int versionCompare(String str1, String str2) {
        String[] v1 = str1.split("\\.");
        String[] v2 = str2.split("\\.");
        int i = 0;
        for (; i < v1.length && i < v2.length; i++) {
            double iv1 = parseVersion(v1[i]);
            double iv2 = parseVersion(v2[i]);

            if (iv1 != iv2) {
                return iv1 - iv2 < 0 ? -1 : 1;
            }
        }
        if (i < v1.length) {
            // "1.0.1", "1.0"
            double iv1 = parseVersion(v1[i]);
            return iv1 < 0 ? -1 : (int) Math.ceil(iv1);
        }
        if (i < v2.length) {
            double iv2 = parseVersion(v2[i]);
            return -iv2 < 0 ? -1 : (int) Math.ceil(iv2);
        }
        return 0;
    }


    public static void main(String[] args) {
        System.out.println("versionCompare(v1.0.0, 1.0.0)");
        System.out.println(versionCompare("v1.0.0", "1.0.0")); // 0

        System.out.println("versionCompare(v1.0.0b, 1.0.0)");
        System.out.println(versionCompare("v1.0.0b", "1.0.0")); // -1

        System.out.println("versionCompare(v1.0.0.preview, 1.0.0)");
        System.out.println(versionCompare("v1.0.0.preview", "1.0.0")); // -1

        System.out.println("versionCompare(v1.0, 1.0.0)");
        System.out.println(versionCompare("v1.0", "1.0.0")); // 0

        System.out.println("versionCompare(ver1.0, 1.0.1)");
        System.out.println(versionCompare("ver1.0", "1.0.1")); // -1
    }
}

It still falls short on cases where you need to compare "alpha" with "beta" though.

Solution 21 - Java

based on https://stackoverflow.com/a/27891752/2642478

class Version(private val value: String) : Comparable<Version> {
    private val splitted by lazy { value.split("-").first().split(".").map { it.toIntOrNull() ?: 0 } }

    override fun compareTo(other: Version): Int {
        for (i in 0 until maxOf(splitted.size, other.splitted.size)) {
            val compare = splitted.getOrElse(i) { 0 }.compareTo(other.splitted.getOrElse(i) { 0 })
            if (compare != 0)
                return compare
        }
        return 0
    }
}

you can use like:

    System.err.println(Version("1.0").compareTo( Version("1.0")))
    System.err.println(Version("1.0") < Version("1.1"))
    System.err.println(Version("1.10") > Version("1.9"))
    System.err.println(Version("1.10.1") > Version("1.10"))
    System.err.println(Version("0.0.1") < Version("1"))

Solution 22 - Java

Maybe someone will be interested in my solution:

class Version private constructor(private val versionString: String) : Comparable<Version> {

    private val major: Int by lazy { versionString.split(".")[0].toInt() }

    private val minor: Int by lazy { versionString.split(".")[1].toInt() }

    private val patch: Int by lazy {
        val splitArray = versionString.split(".")

        if (splitArray.size == 3)
            splitArray[2].toInt()
        else
            0
    }

    override fun compareTo(other: Version): Int {
        return when {
            major > other.major -> 1
            major < other.major -> -1
            minor > other.minor -> 1
            minor < other.minor -> -1
            patch > other.patch -> 1
            patch < other.patch -> -1
            else -> 0
        }
    }

    override fun equals(other: Any?): Boolean {
        if (other == null || other !is Version) return false
        return compareTo(other) == 0
    }

    override fun hashCode(): Int {
        return major * minor * patch
    }

    companion object {
        private fun doesContainsVersion(string: String): Boolean {
            val versionArray = string.split(".")

            return versionArray.size in 2..3
                    && versionArray[0].toIntOrNull() != null
                    && versionArray[1].toIntOrNull() != null
                    && (versionArray.size == 2 || versionArray[2].toIntOrNull() != null)
        }

        fun from(string: String): Version? {
            return if (doesContainsVersion(string)) {
                Version(string)
            } else {
                null
            }
        }
    }
}

Usage:

val version1 = Version.from("3.2")
val version2 = Version.from("3.2.1")
version1 <= version2

Solution 23 - Java

Wrote a little function myself.Simpler Using Lists

 public static boolean checkVersionUpdate(String olderVerison, String newVersion) {
        if (olderVerison.length() == 0 || newVersion.length() == 0) {
            return false;
        }
        List<String> newVerList = Arrays.asList(newVersion.split("\\."));
        List<String> oldVerList = Arrays.asList(olderVerison.split("\\."));

        int diff = newVerList.size() - oldVerList.size();
        List<String> 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<String> oldList, List<String> 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;
    }

Solution 24 - Java

Using Java 8 Stream to replace leading zeroes in components. This code passed all tests on interviewbit.com

public int compareVersion(String A, String B) {
    List<String> strList1 = Arrays.stream(A.split("\\."))
                                           .map(s -> s.replaceAll("^0+(?!$)", ""))
                                           .collect(Collectors.toList());
    List<String> strList2 = Arrays.stream(B.split("\\."))
                                           .map(s -> s.replaceAll("^0+(?!$)", ""))
                                           .collect(Collectors.toList());
    int len1 = strList1.size();
    int len2 = strList2.size();
    int i = 0;
    while(i < len1 && i < len2){
        if (strList1.get(i).length() > strList2.get(i).length()) return 1;
        if (strList1.get(i).length() < strList2.get(i).length()) return -1;
        int result = new Long(strList1.get(i)).compareTo(new Long(strList2.get(i)));
        if (result != 0) return result;
        i++;
    }
    while (i < len1){
        if (!strList1.get(i++).equals("0")) return 1;
    }
    while (i < len2){
        if (!strList2.get(i++).equals("0")) return -1;
    }
    return 0;
}

Solution 25 - Java

For someone who is going to show Force Update Alert based on version number I have a following Idea. This may be used when comparing the versions between Android Current App version and firebase remote config version. This is not exactly the answer for the question asked but this will help someone definitely.

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
public class Main
{
  static String firebaseVersion = "2.1.3"; // or 2.1
  static String appVersion = "2.1.4";
  static List<String> firebaseVersionArray;
  static List<String> appVersionArray;
  static boolean isNeedToShowAlert = false;
  public static void main (String[]args)
  {
    System.out.println ("Hello World");
    firebaseVersionArray = new ArrayList<String>(Arrays.asList(firebaseVersion.split ("\\.")));
    appVersionArray = new ArrayList<String>(Arrays.asList(appVersion.split ("\\.")));
    if(appVersionArray.size() < firebaseVersionArray.size()) {
        appVersionArray.add("0");
    }
    if(firebaseVersionArray.size() < appVersionArray.size()) {
        firebaseVersionArray.add("0");
    }
    isNeedToShowAlert = needToShowAlert(); //Returns false
    System.out.println (isNeedToShowAlert);

  }
  static boolean needToShowAlert() {
      boolean result = false;
      for(int i = 0 ; i < appVersionArray.size() ; i++) {
          if (Integer.parseInt(appVersionArray.get(i)) == Integer.parseInt(firebaseVersionArray.get(i))) {
              continue;
          } else if (Integer.parseInt(appVersionArray.get(i)) > Integer.parseInt(firebaseVersionArray.get(i))){
             result = false;
             break;
          } else if (Integer.parseInt(appVersionArray.get(i)) < Integer.parseInt(firebaseVersionArray.get(i))) {
             result = true;
             break;  
          }
      }
      return result;
  }
}

You can run this code by copy pasting in https://www.onlinegdb.com/online_java_compiler

Solution 26 - Java

public static void main(String[] args) {

	String version1 = "1.0";
	String version2 = "1.0.0";
	String[] version1_splits = version1.split("\\.");
	String[] version2_splits = version2.split("\\.");
	int length = version1_splits.length >= version2_splits.length ? version1_splits.length : version2_splits.length;
	int i=0;
	for(;i<length;i++){
		int version1_int = getValue(version1_splits,i);
		int version2_int = getValue(version2_splits,i);
		if(version1_int > version2_int){
			System.out.println("version1 > version2");
			break;
		}
		else if(version1_int < version2_int){
			System.out.println("version2 > version1");
			break;
		}
		else{
			if(i == length-1)
			System.out.println("version1 = version2");
		}
	}
}

private static int getValue(String[] version1_splits, int i) {
	int temp;
	try{
		temp = Integer.valueOf(version1_splits[i]);
	}
	catch(IndexOutOfBoundsException e){
		temp=0;
	}
	
	return temp;
}

Solution 27 - Java

kotlin:

@kotlin.jvm.Throws(InvalidParameterException::class)
fun String.versionCompare(remoteVersion: String?): Int {
    val remote = remoteVersion?.splitToSequence(".")?.toList() ?: return 1
    val local = this.splitToSequence(".").toList()

    if(local.filter { it.toIntOrNull() != null }.size != local.size) throw InvalidParameterException("version invalid: $this")
    if(remote.filter { it.toIntOrNull() != null }.size != remote.size) throw InvalidParameterException("version invalid: $remoteVersion")

    val totalRange = 0 until kotlin.math.max(local.size, remote.size)
    for (i in totalRange) {
        if (i < remote.size && i < local.size) {
            val result = local[i].compareTo(remote[i])
            if (result != 0) return result
        } else (
            return local.size.compareTo(remote.size)
        )
    }
    return 0
}

Solution 28 - Java

This code try to resolve this type of comparison versions.

> Most of the version specifiers, like >= 1.0, are self-explanatory. The > specifier ~> has a special meaning, best shown by example. ~> 2.0.3 is > identical to >= 2.0.3 and < 2.1. ~> 2.1 is identical to >= 2.1 and < > 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;
}

Solution 29 - Java

I liked the idea from @Peter Lawrey, And i extended it to further limits :

    /**
    * 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<maxLen-s.length(); i++) sb.append('0');
	        sb.append(s);
	    }
	    return sb.toString();
	}
	
    /**
    * Removes trailing zeros of the form '.00.0...00'
    * (and does not remove zeros from, say, '4.1.100')
    **/
	public String removeTrailingZeros(String s){
		int i = s.length()-1;
		int k = s.length()-1;
		while(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);
    }

Hope it helps someone. It passed all test cases in interviewbit and leetcode (need to uncomment two lines in compareVersion function).

Easily tested !

Solution 30 - Java

The most simple solution is using string compare. Remove the unwanded dots or letters in case the version is like v1.0.1Beta using Regex("[^0-9]") and you get for versions like 1.0 and 1.0.1:

String ver1 = "10";
String ver2 = "101";
    
int res = ver1.compareTo(ver2);
System.out.println("res: "+res);

If ver1 is lexicographically less than ver2, a negative number will be returned, 0 if equal or a positive number if ver1 is greater.

The compareTo() method compares two strings lexicographically.

The comparison is based on the Unicode value of each character in the strings.

The method returns 0 if the string is equal to the other string. A value less than 0 is returned if the string is less than the other string (less characters) and a value greater than 0 if the string is greater than the other string (more characters).

Solution 31 - Java

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+<integer read>
	 * 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);
	}
}

Solution 32 - Java

Here's an optimized implementation:

public static final Comparator<CharSequence> VERSION_ORDER = new Comparator<CharSequence>() {

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

};

Result:

"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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBill the LizardView Question on Stackoverflow
Solution 1 - JavaalexView Answer on Stackoverflow
Solution 2 - JavaAlex DeanView Answer on Stackoverflow
Solution 3 - JavagizmoView Answer on Stackoverflow
Solution 4 - JavaPeter LawreyView Answer on Stackoverflow
Solution 5 - JavaRyszard PerkowskiView Answer on Stackoverflow
Solution 6 - JavaMarkus JarderotView Answer on Stackoverflow
Solution 7 - JavaOlivier GrégoireView Answer on Stackoverflow
Solution 8 - JavaAlexView Answer on Stackoverflow
Solution 9 - JavaAlgorithmaticView Answer on Stackoverflow
Solution 10 - JavaMaciej DzikowickiView Answer on Stackoverflow
Solution 11 - JavaSerg BurlakaView Answer on Stackoverflow
Solution 12 - JavaCenk AltiView Answer on Stackoverflow
Solution 13 - JavaStan TowianskiView Answer on Stackoverflow
Solution 14 - JavaMichael GantmanView Answer on Stackoverflow
Solution 15 - JavaMarcello CâmaraView Answer on Stackoverflow
Solution 16 - JavapetrnohejlView Answer on Stackoverflow
Solution 17 - JavaIgor MaznitsaView Answer on Stackoverflow
Solution 18 - JavabanView Answer on Stackoverflow
Solution 19 - JavaG00fYView Answer on Stackoverflow
Solution 20 - JavafelixhView Answer on Stackoverflow
Solution 21 - JavaXanView Answer on Stackoverflow
Solution 22 - JavaArtyom DeynegaView Answer on Stackoverflow
Solution 23 - JavaArpan SharmaView Answer on Stackoverflow
Solution 24 - JavaSaurav SahuView Answer on Stackoverflow
Solution 25 - JavaBharathRaoView Answer on Stackoverflow
Solution 26 - JavaLakshmi Narayana GallaView Answer on Stackoverflow
Solution 27 - JavalegendmoheView Answer on Stackoverflow
Solution 28 - JavagorumsView Answer on Stackoverflow
Solution 29 - JavaAbhinav PuriView Answer on Stackoverflow
Solution 30 - JavaDimView Answer on Stackoverflow
Solution 31 - Javasfrizvi6View Answer on Stackoverflow
Solution 32 - JavaballzakView Answer on Stackoverflow