Рубрики
Без рубрики

Сравнение версий в Java

Изучите несколько способов сравнения строк версий в Java с помощью различных библиотек

Автор оригинала: Anshul Bansal.

1. Обзор

С развитием технологий DevOps стало обычным делом создавать и развертывать приложение несколько раз в день.

Поэтому каждой сборке присваивается уникальный номер версии, чтобы мы могли различать сборки . Иногда возникает необходимость сравнить строки версий программно.

В этой статье мы рассмотрим несколько способов сравнения строк версий в Java с помощью различных библиотек. Наконец, мы напишем пользовательскую программу для обработки общего сравнения строк версий.

2. Использование maven-артефакта

Для начала давайте рассмотрим, как Maven обрабатывает сравнение версий.

2.1. Зависимость Maven

Во-первых, мы добавим последнюю maven-артефакт зависимость Maven к вашему pom.xml :


    org.apache.maven
    maven-artifact
    3.6.3

2.2. Сопоставимая зависимость

Давайте рассмотрим класс ComparableVersion . Он обеспечивает общую реализацию сравнения версий с неограниченным количеством компонентов версий .

Он содержит метод compareTo , и результат сравнения будет больше или меньше 0, если одна версия больше или меньше другой, соответственно:

ComparableVersion version1_1 = new ComparableVersion("1.1");
ComparableVersion version1_2 = new ComparableVersion("1.2");
ComparableVersion version1_3 = new ComparableVersion("1.3");

assertTrue(version1_1.compareTo(version1_2) < 0);
assertTrue(version1_3.compareTo(version1_2) > 0);

Здесь мы можем подтвердить, что версия 1.1 меньше, чем версия 1.2, а версия 1.3 больше, чем версия 1.2.

Однако в результате при сравнении одних и тех же версий мы получим 0:

ComparableVersion version1_1_0 = new ComparableVersion("1.1.0");
assertEquals(0, version1_1.compareTo(version1_1_0));

2.3. Разделители и классификаторы версий

Кроме того, класс ComparableVersion уважает точку (.) и дефис (-) в качестве разделителей, где точка разделяет основную и второстепенную версии, а дефис определяет квалификаторы :

ComparableVersion version1_1_alpha = new ComparableVersion("1.1-alpha");
assertTrue(version1_1.compareTo(version1_1_alpha) > 0);

Здесь мы можем подтвердить, что версия 1.1 больше, чем версия 1.1-alpha.

Есть несколько хорошо известных классификаторов, поддерживаемых сопоставимой версией , таких как alpha , beta , milestone , RC и snapshot (в порядке от самого низкого до самого высокого):

ComparableVersion version1_1_beta = new ComparableVersion("1.1-beta");
ComparableVersion version1_1_milestone = new ComparableVersion("1.1-milestone");
ComparableVersion version1_1_rc = new ComparableVersion("1.1-rc");
ComparableVersion version1_1_snapshot = new ComparableVersion("1.1-snapshot");

assertTrue(version1_1_alpha.compareTo(version1_1_beta) < 0);
assertTrue(version1_1_beta.compareTo(version1_1_milestone) < 0);
assertTrue(version1_1_rc.compareTo(version1_1_snapshot) < 0);
assertTrue(version1_1_snapshot.compareTo(version1_1) < 0);

Кроме того, он позволяет нам определять неизвестные классификаторы и соблюдать их порядок после уже обсужденных известных классификаторов с учетом регистра лексического порядка :

ComparableVersion version1_1_c = new ComparableVersion("1.1-c");
ComparableVersion version1_1_z = new ComparableVersion("1.1-z");
ComparableVersion version1_1_1 = new ComparableVersion("1.1.1");
        
assertTrue(version1_1_c.compareTo(version1_1_z) < 0);
assertTrue(version1_1_z.compareTo(version1_1_1) < 0);

3. Использование gradle-core

Как и Maven, Gradle также имеет встроенную возможность для сравнения версий.

3.1. Зависимость Maven

Во-первых, давайте добавим последнюю зависимость gradle-core Maven из репо Cradle Releases :


    org.gradle
    gradle-core
    6.1.1

3.2. Номер версии

Класс Номер версии , предоставленный Gradle, сравнивает две версии, аналогичные классу Maven ComparableVersion :

VersionNumber version1_1 = VersionNumber.parse("1.1");
VersionNumber version1_2 = VersionNumber.parse("1.2");
VersionNumber version1_3 = VersionNumber.parse("1.3");

assertTrue(version1_1.compareTo(version1_2) < 0);
assertTrue(version1_3.compareTo(version1_2) > 0);

VersionNumber version1_1_0 = VersionNumber.parse("1.1.0");
assertEquals(0, version1_1.compareTo(version1_1_0));

3.3. Компоненты версии

В отличие от класса ComparableVersion , класс VersionNumber поддерживает только пять компонентов версии — Major , Minor , Micro , Patch и Квалификатор :

VersionNumber version1_1_1_1_alpha = VersionNumber.parse("1.1.1.1-alpha"); 
assertTrue(version1_1.compareTo(version1_1_1_1_alpha) < 0); 

VersionNumber version1_1_beta = VersionNumber.parse("1.1.0.0-beta"); 
assertTrue(version1_1_beta.compareTo(version1_1_1_1_alpha) < 0);

3.4. Схемы версий

Кроме того, Номер версии поддерживает несколько различных схем версий, таких как Major.Незначительный.Микро-квалификатор и Мажор.Незначительный.Micro.Patch-Квалификатор :

VersionNumber version1_1_1_snapshot = VersionNumber.parse("1.1.1-snapshot");
assertTrue(version1_1_1_1_alpha.compareTo(version1_1_1_snapshot) < 0);

4. Использование ядра Джексона

4.1. Зависимость Maven

Подобно другим зависимостям, давайте добавим последнюю зависимость jackson-core Maven в ваш pom.xml :


    com.fasterxml.jackson.core
    jackson-core
    2.11.1

4.2. Версия

Затем мы можем изучить класс Jackson ‘s Version , который может содержать информацию о версии компонента вместе с необязательными значениями groupId и artifactId .

Поэтому конструктор класса Version позволяет нам определить groupId и artifactId, наряду с такими компонентами, как Major , Minor и Patch :

public Version (int major, int minor, int patchLevel, String snapshotInfo, String groupId, String artifactId) {
    //...
}

Итак, давайте сравним несколько версий, используя класс Version :

Version version1_1 = new Version(1, 1, 0, null, null, null);
Version version1_2 = new Version(1, 2, 0, null, null, null);
Version version1_3 = new Version(1, 3, 0, null, null, null);

assertTrue(version1_1.compareTo(version1_2) < 0);
assertTrue(version1_3.compareTo(version1_2) > 0);

Version version1_1_1 = new Version(1, 1, 1, null, null, null);
assertTrue(version1_1.compareTo(version1_1_1) < 0);

4.3. Компонент Информации о моментальном снимке

Компонент snapshot Info не используется при сравнении двух версий:

Version version1_1_snapshot = new Version(1, 1, 0, "snapshot", null, null); 
assertEquals(0, version1_1.compareTo(version1_1_snapshot));

Кроме того, класс Version предоставляет метод is Snapshot для проверки того, содержит ли версия компонент моментального снимка:

assertTrue(version1_1_snapshot.isSnapshot());

4.4. Компоненты groupId и artifactId

Кроме того, этот класс сравнивает лексический порядок компонентов groupId и artifactId version :

Version version1_1_maven = new Version(1, 1, 0, null, "org.apache.maven", null);
Version version1_1_gradle = new Version(1, 1, 0, null, "org.gradle", null);
assertTrue(version1_1_maven.compareTo(version1_1_gradle) < 0);

5. Использование Semver

Библиотека Semver4j позволяет нам следовать правилам спецификации семантического управления версиями в Java.

5.1. Зависимость Maven

Во-первых, мы добавим последнюю зависимость semver4j Maven:


    com.vdurmont
    semver4j
    3.1.0

5.2. Семвер

Затем мы можем использовать класс Semver для определения версии:

Semver version1_1 = new Semver("1.1.0");
Semver version1_2 = new Semver("1.2.0");
Semver version1_3 = new Semver("1.3.0");

assertTrue(version1_1.compareTo(version1_2) < 0);
assertTrue(version1_3.compareTo(version1_2) > 0);

Внутренне он анализирует версию на такие компоненты, как Major , Minor и Patch .

5.3. Сравнение версий

Кроме того, класс Semver поставляется с различными встроенными методами, такими как isGreaterThan , Ниже, чем , и равен для сравнения версий:

Semver version1_1_alpha = new Semver("1.1.0-alpha"); 
assertTrue(version1_1.isGreaterThan(version1_1_alpha)); 

Semver version1_1_beta = new Semver("1.1.0-beta"); 
assertTrue(version1_1_alpha.isLowerThan(version1_1_beta)); 

assertTrue(version1_1.isEqualTo("1.1.0"));

Аналогично, он предоставляет метод diff , который возвращает основное различие между двумя версиями:

assertEquals(VersionDiff.MAJOR, version1_1.diff("2.1.0"));
assertEquals(VersionDiff.MINOR, version1_1.diff("1.2.3"));
assertEquals(VersionDiff.PATCH, version1_1.diff("1.1.1"));

5.4. Стабильность версий

Кроме того, класс Semver поставляется с методом is Stable для проверки стабильности версии, определяемой наличием или отсутствием суффикса:

assertTrue(version1_1.isStable());
assertFalse(version1_1_alpha.isStable());

6. Индивидуальное Решение

Мы видели несколько решений для сравнения строк версий. Если они не работают для конкретного случая использования, нам, возможно, придется написать собственное решение.

Вот простой пример, который работает для некоторых основных случаев — он всегда может быть расширен, если нам нужно что-то большее.

Идея здесь состоит в том, чтобы маркировать строки версий с помощью разделителя точек, а затем сравнить целочисленное преобразование каждого String токена, начиная с левого. Если целочисленное значение токена совпадает, изучите следующий токен, продолжая этот шаг до тех пор, пока мы не найдем разницу (или пока не достигнем последнего токена в любой строке):

public static int compareVersions(String version1, String version2) {
    int comparisonResult = 0;
    
    String[] version1Splits = version1.split("\\.");
    String[] version2Splits = version2.split("\\.");
    int maxLengthOfVersionSplits = Math.max(version1Splits.length, version2Splits.length);

    for (int i = 0; i < maxLengthOfVersionSplits; i++){
        Integer v1 = i < version1Splits.length ? Integer.parseInt(version1Splits[i]) : 0;
        Integer v2 = i < version2Splits.length ? Integer.parseInt(version2Splits[i]) : 0;
        int compare = v1.compareTo(v2);
        if (compare != 0) {
            comparisonResult = compare;
            break;
        }
    }
    return comparisonResult;
}

Давайте проверим наше решение, сравнив несколько версий:

assertTrue(VersionCompare.compareVersions("1.0.1", "1.1.2") < 0);
assertTrue(VersionCompare.compareVersions("1.0.1", "1.10") < 0);
assertTrue(VersionCompare.compareVersions("1.1.2", "1.0.1") > 0);
assertTrue(VersionCompare.compareVersions("1.1.2", "1.2.0") < 0);
assertEquals(0, VersionCompare.compareVersions("1.3.0", "1.3"));

Этот код имеет ограничение, заключающееся в том, что он может сравнивать только номер версии, состоящий из целых чисел, разделенных точками.

Поэтому для сравнения буквенно-цифровых строк версий мы можем использовать регулярное выражение для разделения алфавитов и сравнения лексического порядка.

7. Заключение

В этой статье мы рассмотрели различные способы сравнения строк версий в Java.

Сначала мы рассмотрели встроенные решения, предоставляемые фреймворками сборки, такими как Maven и Gradle, используя зависимости maven-artifact и gradle-core соответственно. Затем мы изучили возможности сравнения версий библиотек jackson-core и semver4j .

Наконец, мы написали пользовательское решение для сравнения строк общей версии.

Как обычно, все реализации кода доступны на GitHub .