eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Overview

With the advancement of DevOps technologies, it’s common to build and deploy an application multiple times in a day.

Therefore, every build is assigned a unique version number so we can distinguish between builds. Sometimes, a need arises to compare the version strings programmatically.

In this article, we’ll explore a few ways to compare version strings in Java through various libraries. At last, we’ll write a custom program to handle generic version-string comparison.

2. Using maven-artifact

To start with, let’s explore how Maven handles version comparison.

2.1. Maven Dependency

First, we’ll add the latest maven-artifact Maven dependency to our pom.xml:

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

2.2. ComparableVersion

Let’s explore the ComparableVersion class. It provides a generic implementation of version comparison with an unlimited number of version components.

It contains a compareTo method, and the result of the comparison will be greater than or less than 0 when one version is greater than or less than the other, respectively:

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

Here, we can confirm that the 1.1 version is less than the 1.2 version, and the 1.3 version is greater than the 1.2 version.

However, we will get 0 as a result when comparing the same versions:

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

2.3. Version Separators and Qualifiers

Additionally, the ComparableVersion class respects the dot (.) and hyphen (-) as separators, where the dot separates major and minor versions, and the hyphen defines qualifiers:

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

Here, we can confirm that the 1.1 version is greater than the 1.1-alpha version.

There are a few well-known qualifiers supported by the ComparableVersion like the alpha, beta, milestone, RC, and snapshot (in the order of lowest to highest):

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

Also, it allows us to define unknown qualifiers and respects their order, after the already discussed known qualifiers, with case-insensitive lexical order:

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

Like Maven, Gradle also has the built-in capability to handle version comparison.

3.1. Maven Dependency

First, let’s add the latest gradle-core Maven dependency from the Gradle Releases repo:

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

3.2. VersionNumber

The VersionNumber class provided by Gradle compares two versions, similar to Maven’s ComparableVersion class:

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

Unlike the ComparableVersion class, the VersionNumber class supports only five version components — Major, Minor, Micro, Patch, and 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. Version Schemes

Also, VersionNumber supports a couple of different version schemes like Major.Minor.Micro-Qualifier and 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. Using jackson-core

4.1. Maven Dependency

Similar to other dependencies, let’s add the latest jackson-core Maven dependency to our pom.xml:

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

4.2. Version

Then, we can examine Jackson‘s Version class, which can hold versioning information of a component along with the optional groupId and artifactId values.

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);
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. The snapshotInfo Component

The snapshotInfo component is used while comparing two versions:

Version version1_1_snapshot = new Version(1, 1, 0, "snapshot", null, null); 
assertEquals(1, 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:

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

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);
assertTrue(version1_3.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.0.1") > 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.

At first, we examined built-in solutions provided by build frameworks like Maven and Gradle, using the maven-artifact and gradle-core dependencies, respectively. Then, we explored version comparison features of the jackson-core and semver4j libraries.

Last, we wrote a custom solution for generic version string comparison.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – LS – NPI (cat=Java)
announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)