Versievergelijking in Java

1. Overzicht

Met de vooruitgang van DevOps-technologieën is het gebruikelijk om een ​​applicatie meerdere keren per dag te bouwen en te implementeren.

Daarom elke build krijgt een uniek versienummer toegewezen, zodat we onderscheid kunnen maken tussen builds. Soms ontstaat de behoefte om de versiereeksen programmatisch te vergelijken.

In dit artikel zullen we enkele manieren onderzoeken om versiereeksen in Java te vergelijken via verschillende bibliotheken. Ten slotte zullen we een aangepast programma schrijven om generieke versie-string-vergelijking af te handelen.

2. Met behulp van maven-artefact

Laten we om te beginnen eens kijken hoe Maven omgaat met versievergelijking.

2.1. Afhankelijkheid van Maven

Eerst voegen we de laatste toe maven-artefact Maven afhankelijkheid van onze pom.xml:

 org.apache.maven maven-artefact 3.6.3 

2.2. Vergelijkbare versie

Laten we de Vergelijkbare versie klasse. Het biedt een generieke implementatie van versievergelijking met een onbeperkt aantal versiecomponenten.

Het bevat een vergelijk met methode, en het resultaat van de vergelijking zal groter of kleiner zijn dan 0 wanneer de ene versie groter is dan of kleiner is dan de andere:

ComparableVersion version1_1 = nieuwe ComparableVersion ("1.1"); ComparableVersion version1_2 = nieuwe ComparableVersion ("1.2"); ComparableVersion version1_3 = nieuwe ComparableVersion ("1.3"); assertTrue (version1_1.compareTo (version1_2) 0);

Hier kunnen we bevestigen dat versie 1.1 lager is dan versie 1.2 en versie 1.3 hoger is dan versie 1.2.

Als resultaat krijgen we echter 0 als we dezelfde versies vergelijken:

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

2.3. Versie-scheidingstekens en kwalificaties

Bovendien is het Vergelijkbare versie klasse respecteert de punt (.) en het koppelteken (-) als scheidingstekens, waarbij de punt scheidt hoofd- en secundaire versies, en het koppelteken definieert kwalificaties:

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

Hier kunnen we bevestigen dat de 1.1-versie hoger is dan de 1.1-alpha-versie.

Er zijn een paar bekende kwalificaties die worden ondersteund door de Vergelijkbare versie zoals de alpha, bèta, mijlpaal, RC, en momentopname (in de volgorde van laag naar hoog):

ComparableVersion version1_1_beta = nieuwe ComparableVersion ("1.1-beta"); ComparableVersion version1_1_milestone = nieuwe ComparableVersion ("1.1-mijlpaal"); ComparableVersion version1_1_rc = nieuwe ComparableVersion ("1.1-rc"); ComparableVersion version1_1_snapshot = nieuwe 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);

Ook het stelt ons in staat om onbekende kwalificaties te definiëren en respecteert hun volgorde, na de reeds besproken bekende kwalificaties, met hoofdletterongevoelige lexicale volgorde:

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

3. Met behulp van gradle-core

Net als Maven heeft Gradle ook de ingebouwde mogelijkheid om versievergelijking af te handelen.

3.1. Afhankelijkheid van Maven

Laten we eerst het laatste toevoegen gradle-core Maven-afhankelijkheid van de Gradle Releases-repo:

 org.gradle gradle-core 6.1.1 

3.2. Versienummer

De Versienummer klasse geleverd door Gradle vergelijkt twee versies, vergelijkbaar met die van Maven Vergelijkbare versie klasse:

VersionNumber version1_1 = VersionNumber.parse ("1.1"); VersionNumber version1_2 = VersionNumber.parse ("1.2"); Versienummer version1_3 = Versienummer.parse ("1.3"); assertTrue (version1_1.compareTo (version1_2) 0); Versienummer version1_1_0 = Versienummer.parse ("1.1.0"); assertEquals (0, version1_1.compareTo (version1_1_0)); 

3.3. Versiecomponenten

In tegenstelling tot de Vergelijkbare versie klasse, de Versienummer klasse ondersteunt slechts vijf versiecomponenten - Majoor, Minor, Micro, Patch, en Kwalificatiewedstrijd:

Versienummer version1_1_1_1_alpha = Versienummer.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. Versieschema's

Ook, Versienummer ondersteunt een aantal verschillende versieschema's zoals Major.Minor.Micro-Qualifier en 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. Met behulp van jackson-core

4.1. Afhankelijkheid van Maven

Laten we, net als bij andere afhankelijkheden, het laatste toevoegen jackson-core Maven afhankelijkheid van onze pom.xml:

 com.fasterxml.jackson.core jackson-core 2.11.1 

4.2. Versie

Vervolgens kunnen we die van Jackson onderzoeken Versie class, die versie-informatie van een component kan bevatten, samen met het optionele groupId en artefact-id waarden.

Daarom is de constructor van de Versie klasse stelt ons in staat om te definiëren groupId en artefact-id, samen met componenten zoals Majoor, Minor, en Patch:

openbare versie (int major, int minor, int patchLevel, String snapshotInfo, String groupId, String artifactId) {// ...}

Laten we dus een paar versies vergelijken met de Versie klasse:

Versie version1_1 = nieuwe versie (1, 1, 0, null, null, null); Versie version1_2 = nieuwe versie (1, 2, 0, null, null, null); Versie version1_3 = nieuwe versie (1, 3, 0, null, null, null); assertTrue (version1_1.compareTo (version1_2) 0); Versie version1_1_1 = nieuwe versie (1, 1, 1, null, null, null); assertTrue (version1_1.compareTo (version1_1_1) <0);

4.3. De snapshotInfo Component

De snapshotInfo component wordt niet gebruikt bij het vergelijken van twee versies:

Versie version1_1_snapshot = nieuwe versie (1, 1, 0, "snapshot", null, null); assertEquals (0, version1_1.compareTo (version1_1_snapshot));

Bovendien is het Versie klasse biedt de isSnapshot methode om te controleren of de versie een snapshot-component bevat:

assertTrue (version1_1_snapshot.isSnapshot ());

4.4. De groupId en artefact-id Componenten

Deze klasse vergelijkt ook de lexicale volgorde van de groupId en artefact-id versie componenten:

Versie version1_1_maven = nieuwe versie (1, 1, 0, null, "org.apache.maven", null); Versie version1_1_gradle = nieuwe versie (1, 1, 0, null, "org.gradle", null); assertTrue (version1_1_maven.compareTo (version1_1_gradle) <0);

5. Met behulp van Semver4J

De Semver4j library stelt ons in staat om de regels van de semantische versie-specificatie in Java te volgen.

5.1. Afhankelijkheid van Maven

Eerst voegen we de laatste toe semver4j Maven-afhankelijkheid:

 com.vdurmont semver4j 3.1.0 

5.2. Semver

Vervolgens kunnen we de Semver class om een ​​versie te definiëren:

Semver version1_1 = nieuwe Semver ("1.1.0"); Semver version1_2 = nieuwe Semver ("1.2.0"); Semver version1_3 = nieuwe Semver ("1.3.0"); assertTrue (version1_1.compareTo (version1_2) 0); 

Intern parseert het een versie in componenten zoals Majoor, Minor, en Patch.

5.3. Versievergelijking

Ook de Semver class wordt geleverd met verschillende ingebouwde methoden zoals is groter dan, isLowerThan, en is gelijk aan voor versievergelijking:

Semver version1_1_alpha = nieuwe Semver ("1.1.0-alpha"); assertTrue (version1_1.isGreaterThan (version1_1_alpha)); Semver version1_1_beta = nieuwe Semver ("1.1.0-beta"); assertTrue (version1_1_alpha.isLowerThan (version1_1_beta)); assertTrue (version1_1.isEqualTo ("1.1.0"));

Evenzo biedt het de diff methode die het belangrijkste verschil tussen de twee versies retourneert:

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. Versie stabiliteit

Ook de Semver klasse wordt geleverd met de isStable methode om de stabiliteit van een versie te controleren, bepaald door de aan- of afwezigheid van een achtervoegsel:

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

6. Aangepaste oplossing

We hebben een paar oplossingen gezien om de versiereeksen te vergelijken. Als ze niet werken voor een specifieke use-case, moeten we mogelijk een aangepaste oplossing schrijven.

Hier is een eenvoudig voorbeeld dat werkt voor enkele eenvoudige gevallen - het kan altijd worden uitgebreid als we iets meer nodig hebben.

Het idee hier is om de versiereeksen te tokeniseren met behulp van een puntscheidingsteken, en vervolgens de gehele conversie van elke Draad token, beginnend van links. Als de gehele waarde van het token hetzelfde is, onderzoek dan het volgende token en ga door met deze stap totdat we een verschil vinden (of totdat we het laatste token in een van beide tekenreeksen bereiken):

openbare statische 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; Geheel getal v2 = i <version2Splits.length? Integer.parseInt (version2Splits [i]): 0; int vergelijk = v1.compareTo (v2); if (vergelijk! = 0) {comparisonResult = vergelijk; breken; }} return comparisonResult; }

Laten we onze oplossing verifiëren door een paar versies te vergelijken:

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

Deze code heeft een beperking dat het alleen een versienummer kan vergelijken dat bestaat uit gehele getallen die worden gescheiden door punten.

Daarom kunnen we voor het vergelijken van alfanumerieke versiereeksen een reguliere expressie gebruiken om alfabetten te scheiden en de lexicale volgorde te vergelijken.

7. Conclusie

In dit artikel hebben we verschillende manieren onderzocht om versiereeksen in Java te vergelijken.

In eerste instantie hebben we de ingebouwde oplossingen onderzocht die worden geboden door build-frameworks zoals Maven en Gradle, met behulp van de maven-artefact en gradle-core afhankelijkheden, respectievelijk. Vervolgens hebben we de functies voor versievergelijking van het jackson-core en semver4j bibliotheken.

Als laatste schreven we een aangepaste oplossing voor het vergelijken van generieke versies.

Zoals gewoonlijk zijn alle code-implementaties beschikbaar op GitHub.