Perbandingan Versi di Java

1. Ikhtisar

Dengan kemajuan teknologi DevOps, membangun dan menerapkan aplikasi beberapa kali dalam sehari merupakan hal yang umum.

Oleh karena itu, setiap build diberi nomor versi unik sehingga kita dapat membedakan antara build . Terkadang, kebutuhan muncul untuk membandingkan string versi secara terprogram.

Dalam artikel ini, kita akan mempelajari beberapa cara untuk membandingkan string versi di Java melalui berbagai pustaka. Akhirnya, kami akan menulis program khusus untuk menangani perbandingan string versi umum.

2. Menggunakan maven-artifact

Untuk memulainya, mari kita telusuri bagaimana Maven menangani perbandingan versi.

2.1. Ketergantungan Maven

Pertama, kami akan menambahkan dependensi Maven -artefak Maven terbaru ke pom.xml kami :

 org.apache.maven maven-artifact 3.6.3 

2.2. ComparableVersion

Mari jelajahi kelas ComparableVersion . Ini menyediakan implementasi umum dari perbandingan versi dengan jumlah komponen versi yang tidak terbatas .

Ini berisi compareTo metode, dan hasil perbandingan akan lebih besar dari atau kurang dari 0 ketika satu versi lebih besar dari atau kurang dari yang lain, masing-masing:

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

Di sini, kami dapat mengonfirmasi bahwa versi 1.1 lebih kecil dari versi 1.2, dan versi 1.3 lebih besar dari versi 1.2.

Namun, kami akan mendapatkan 0 sebagai hasil saat membandingkan versi yang sama:

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

2.3. Pemisah dan Kualifikasi Versi

Selain itu, kelas ComparableVersion menghormati titik (.) Dan tanda hubung (-) sebagai pemisah, di mana titik memisahkan versi mayor dan minor, dan tanda hubung menentukan kualifikasi :

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

Di sini, kami dapat mengonfirmasi bahwa versi 1.1 lebih besar dari versi 1.1-alpha.

Ada beberapa qualifier terkenal yang didukung oleh ComparableVersion seperti alpha , beta , milestone , RC , dan snapshot (dari urutan terendah ke tertinggi):

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

Selain itu, ini memungkinkan kita untuk menentukan qualifier yang tidak diketahui dan menghormati urutan mereka, setelah qualifier diketahui yang sudah dibahas, dengan urutan leksikal yang tidak peka huruf besar / kecil :

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. Menggunakan gradle-core

Seperti Maven, Gradle juga memiliki kemampuan bawaan untuk menangani perbandingan versi.

3.1. Ketergantungan Maven

Pertama, mari tambahkan dependensi Maven gradle-core terbaru dari repo Gradle Releases:

 org.gradle gradle-core 6.1.1 

3.2. VersionNumber

Kelas VersionNumber yang disediakan oleh Gradle membandingkan dua versi, mirip dengan kelas ComparableVersion Maven :

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); VersionNumber version1_1_0 = VersionNumber.parse("1.1.0"); assertEquals(0, version1_1.compareTo(version1_1_0)); 

3.3. Komponen Versi

Tidak seperti kelas ComparableVersion , kelas VersionNumber hanya mendukung lima komponen versi - Major , Minor , Micro , Patch , dan Qualifier :

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. Skema Versi

Selain itu, VersionNumber mendukung beberapa skema versi yang berbeda seperti Major.Minor.Micro-Qualifier dan Major.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. Menggunakan jackson-core

4.1. Ketergantungan Maven

Mirip dengan dependensi lainnya, mari tambahkan dependensi jackson-core Maven terbaru ke pom.xml kami :

 com.fasterxml.jackson.core jackson-core 2.11.1 

4.2. Versi: kapan

Kemudian, kita dapat mempelajari Jackson Versi kelas, yang dapat menampung versioning informasi dari komponen bersama dengan opsional groupId dan artifactId nilai-nilai .

Therefore, the constructor of the Version class allows us to define groupId and artifactId, along with components like Major, Minor, and Patch:

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

So, let's compare a few versions using the Version class:

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); Version version1_1_1 = new Version(1, 1, 1, null, null, null); assertTrue(version1_1.compareTo(version1_1_1) < 0);

4.3. The snapshotInfo Component

The snapshotInfo component isn't used while comparing two versions:

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

Additionally, the Version class provides the isSnapshot method to check if the version contains a snapshot component:

assertTrue(version1_1_snapshot.isSnapshot());

4.4. The groupId and artifactId Components

Also, this class compares the lexical order of the groupId and artifactId version components:

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. Using Semver4J

The Semver4j library allows us to follow the rules of the semantic versioning specification in Java.

5.1. Maven Dependency

First, we'll add the latest semver4j Maven dependency:

 com.vdurmont semver4j 3.1.0 

5.2. Semver

Then, we can use the Semver class to define a version:

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

Internally, it parses a version into components like Major, Minor, and Patch.

5.3. Version Comparison

Also, the Semver class comes with various built-in methods like isGreaterThan, isLowerThan, and isEqualTo for version comparison:

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

Likewise, it provides the diff method that returns the main difference between the two versions:

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. Version Stability

Also, the Semver class comes with the isStable method to check the stability of a version, determined by the presence or absence of a suffix:

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

6. Custom Solution

We've seen a few solutions to compare the version strings. If they don't work for a specific use-case, we might have to write a custom solution.

Here's a simple example that works for some basic cases — it can always be extended if we need something more.

The idea here is to tokenize the version strings using a dot delimiter, and then compare integer conversion of every String token, beginning from the left. If the token's integer value is the same, examine the next token, continuing this step until we find a difference (or until we reach the last token in either 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; }

Let's verify our solution by comparing a few versions:

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.2.0") < 0); assertEquals(0, VersionCompare.compareVersions("1.3.0", "1.3"));

This code has a limitation that it can only compare a version number made of integers delimited by dots.

Therefore, for comparing alphanumeric version strings, we can use a regular expression to segregate alphabets and compare the lexical order.

7. Conclusion

In this article, we looked into various ways to compare version strings in Java.

Pertama-tama, kami memeriksa solusi bawaan yang disediakan oleh framework build seperti Maven dan Gradle, masing-masing menggunakan dependensi maven-artifact dan gradle-core . Kemudian, kami menjelajahi fitur perbandingan versi pustaka jackson-core dan semver4j .

Terakhir, kami menulis solusi khusus untuk perbandingan string versi generik.

Seperti biasa, semua implementasi kode tersedia di GitHub.