是否有比较版本号的标准习语?我不能直接使用String compareTo,因为我还不知道点释放的最大数量是多少。我需要比较版本,并有以下保持正确:

1.0 < 1.1
1.0.1 < 1.1
1.9 < 1.10

当前回答

我创建了一个简单的实用程序,使用语义版本约定在Android平台上比较版本。所以它只适用于X.Y.Z (Major.Minor.Patch)格式的字符串,其中X、Y和Z是非负整数。你可以在我的GitHub上找到它。

方法version . compareversions (String v1, String v2)比较两个版本字符串。如果版本相等则返回0,如果版本v1在版本v2之前则返回1,如果版本v1在版本v2之后则返回-1,如果版本格式无效则返回-2。

其他回答

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

您需要规范化版本字符串,以便对它们进行比较。类似的

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'

科特林:

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

如果你的项目中已经有Jackson,你可以使用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);
    }
}

也许有人会对我的解决方案感兴趣:

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

用法:

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