1. 概要

DevOpsテクノロジーの進歩に伴い、アプリケーションを1日に複数回ビルドしてデプロイするのが一般的です。

したがって、すべてのビルドには一意のバージョン番号が割り当てられているため、ビルドを区別できます。 プログラムでバージョン文字列を比較する必要が生じる場合があります。

この記事では、さまざまなライブラリを介してJavaのバージョン文字列を比較するいくつかの方法について説明します。 最後に、一般的なバージョンと文字列の比較を処理するカスタムプログラムを作成します。

2. maven-artifactを使用する

まず、Mavenがバージョン比較を処理する方法を調べてみましょう。

2.1. Mavenの依存関係

まず、最新の maven-artifactMaven依存関係をpom.xmlに追加します。

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

2.2. CompareableVersion

CompareableVersionクラスを調べてみましょう。 これは、バージョン比較の一般的な実装と無制限の数のバージョンコンポーネントを提供します。

これには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. バージョンセパレータと修飾子

さらに、 CompareableVersion クラスは、ドット(。)とハイフン(-)を区切り文字として尊重します。ここで、ドットはメジャーバージョンとマイナーバージョンを区切り、ハイフンは修飾子を定義します。

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

ここで、1.1バージョンが1.1アルファバージョンよりも大きいことを確認できます。

alpha beta milestone RC など、CompareableVersionでサポートされている有名な修飾子がいくつかあります。 ]、および 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リリースリポジトリから最新のgradle-coreMaven依存関係を追加しましょう。

<dependency>
    <groupId>org.gradle</groupId>
    <artifactId>gradle-core</artifactId>
    <version>6.1.1</version>
</dependency>

3.2. VersionNumber

Gradleが提供するVersionNumberクラスは、MavenのCompareableVersionクラスと同様に2つのバージョンを比較します。

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. バージョンコンポーネント

CompareableVersion クラスとは異なり、 VersionNumber クラスは、 Major Minor Micro 、の5つのバージョンコンポーネントのみをサポートします。 パッチ、および修飾子

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. バージョンスキーム

また、 VersionNumber は、Major.Minor.Micro-QualifierMajor.Minor.Micro.Patch-Qualifierなどのいくつかの異なるバージョンスキームをサポートしています。

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

4. jackson-coreを使用する

4.1. Mavenの依存関係

他の依存関係と同様に、最新の jackson-coreMaven依存関係をpom.xmlに追加しましょう。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.11.1</version>
</dependency>

4.2. バージョン

次に、 Jackson Versionクラスを調べることができます。このクラスは、オプションのgroupIdおよびartifactId値とともにコンポーネントのバージョン情報を保持できます。

したがって、 Version クラスのコンストラクターを使用すると、 groupIdおよびartifactId、を、 Major Minorなどのコンポーネントとともに定義できます。 、およびパッチ

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. snapshotInfoコンポーネント

snapshotInfo コンポーネントは、2つのバージョンを比較している間は使用されません。

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

さらに、 Version クラスは、バージョンにスナップショットコンポーネントが含まれているかどうかを確認するためのisSnapshotメソッドを提供します。

assertTrue(version1_1_snapshot.isSnapshot());

4.4. groupIdおよびartifactIdコンポーネント

また、このクラスは、groupIdartifactIdバージョンコンポーネントの辞書式順序を比較します。

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. Semver4Jを使用する

Semver4j ライブラリを使用すると、Javaのセマンティックバージョニング仕様のルールに従うことができます。

5.1. Mavenの依存関係

まず、最新の semver4jMaven依存関係を追加します。

<dependency>
    <groupId>com.vdurmont</groupId>
    <artifactId>semver4j</artifactId>
    <version>3.1.0</version>
</dependency>

5.2. Semver

次に、 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 isLowerThan isEqualToなどのさまざまな組み込みメソッドが付属しています。

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

同様に、2つのバージョンの主な違いを返す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 クラスには、 isStable メソッドが付属しており、接尾辞の有無によって決定されるバージョンの安定性をチェックします。

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-artifactgradle-coreの依存関係をそれぞれ使用して、MavenやGradleなどのビルドフレームワークによって提供される組み込みソリューションを調べました。 次に、jackson-coreおよびsemver4jライブラリのバージョン比較機能について説明しました。

最後に、汎用バージョンの文字列を比較するためのカスタムソリューションを作成しました。

いつものように、すべてのコード実装はGitHub利用できます。