Perbandingan Versi di Java

1. Gambaran keseluruhan

Dengan kemajuan teknologi DevOps, adalah biasa untuk membuat dan menyebarkan aplikasi berkali-kali dalam sehari.

Oleh itu, setiap binaan diberikan nombor versi unik sehingga kita dapat membezakan antara binaan . Kadang kala, timbul keperluan untuk membandingkan rentetan versi secara program.

Dalam artikel ini, kita akan meneroka beberapa cara untuk membandingkan rentetan versi di Java melalui pelbagai perpustakaan. Akhirnya, kami akan menulis program khusus untuk menangani perbandingan rentetan versi generik.

2. Menggunakan artifak maven

Sebagai permulaan, mari kita terokai bagaimana Maven menangani perbandingan versi.

2.1. Ketergantungan Maven

Pertama, kami akan menambahkan pergantungan maven- artifact Maven terbaru ke pom.xml kami :

 org.apache.maven maven-artifact 3.6.3 

2.2. Perbandingan Berbanding

Mari kita terokai kelas ComparableVersion . Ini menyediakan pelaksanaan perbandingan versi secara umum dengan jumlah komponen versi yang tidak terhad .

Ia mengandungi kaedah perbandinganTo , dan hasil perbandingan akan lebih besar daripada atau kurang dari 0 ketika satu versi lebih besar daripada atau kurang daripada 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, kita dapat mengesahkan bahawa versi 1.1 kurang dari versi 1.2, dan versi 1.3 lebih besar daripada versi 1.2.

Walau bagaimanapun, kami akan mendapat 0 hasilnya ketika 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 Kelayakan Versi

Selain itu, kelas ComparableVersion menghormati titik (.) Dan tanda hubung (-) sebagai pemisah, di mana titik memisahkan versi utama dan kecil, dan tanda hubung menentukan kelayakan :

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

Di sini, kita dapat mengesahkan bahawa versi 1.1 lebih besar daripada versi 1.1-alpha.

Terdapat beberapa kelayakan terkenal yang disokong oleh ComparableVersion seperti alpha , beta , tonggak sejarah , RC , dan snapshot (mengikut urutan terendah hingga 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);

Juga, ini membolehkan kita menentukan kelayakan yang tidak diketahui dan menghormati urutan mereka, setelah kelayakan yang telah diketahui, dengan susunan leksikal 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 mempunyai kemampuan terpadu untuk menangani perbandingan versi.

3.1. Ketergantungan Maven

Pertama, mari tambah pergantungan Maven gradle-core terbaru dari repo Gradle Releases:

 org.gradle gradle-core 6.1.1 

3.2. Nombor Versi

The VERSIONNUMBER kelas disediakan oleh Gradle membandingkan dua versi, sama seperti Maven ini ComparableVersion kelas:

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 ComparableVersion kelas, VERSIONNUMBER kelas hanya menyokong lima komponen versi - Major , Minor , Micro , Patch , dan Kelayakan :

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

Juga, VERSIONNUMBER menyokong beberapa skim versi yang berbeza seperti Major.Minor.Micro-Kelayakan dan Major.Minor.Micro.Patch-Kelayakan :

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

Sama seperti kebergantungan lain, mari tambahkan kebergantungan jackson-core Maven terbaru ke pom.xml kami :

 com.fasterxml.jackson.core jackson-core 2.11.1 

4.2. Versi

Kemudian, kita boleh memeriksa Jackson Versi kelas, yang boleh memegang pemversian maklumat komponen yang bersama-sama dengan pilihan groupId dan artifactId 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.

Pada mulanya, kami meneliti penyelesaian terbina dalam yang disediakan oleh kerangka kerja binaan seperti Maven dan Gradle , masing-masing menggunakan pergantungan artifak dan inti gradasi . Kemudian, kami meneroka ciri perbandingan versi perpustakaan jackson-core dan semver4j .

Terakhir, kami menulis penyelesaian khusus untuk perbandingan rentetan versi generik.

Seperti biasa, semua pelaksanaan kod tersedia di GitHub.