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. Introduction

In this tutorial, we’ll explore various ways of sorting a list alphabetically in Java.

First, we’ll start with the Collections class and then use the Comparator interface. We’ll also use List’s API to sort alphabetically followed by the streams and finally use TreeSet.

Additionally, we’ll be expanding our examples to explore several different scenarios, including sorting lists based on a specific locale, sorting accented lists, and using RuleBasedCollator to define our custom sorting rules.

2. Sorting Using Collections Class

First, let’s see how we can sort a list using the Collections class.

The Collections class provides a static, overloaded method sort that can take the list and sort it either in natural order or we can provide the custom sorting logic using a Comparator.

2.1. Sorting in Natural/Lexicographic Order

First, we’ll define the input list:

private static List<String> INPUT_NAMES = Arrays.asList("john", "mike", "usmon", "ken", "harry");

Now we’ll use the Collections class first to sort the list in the natural order, also known as lexicographic order:

@Test
void givenListOfStrings_whenUsingCollections_thenListIsSorted() {
    Collections.sort(INPUT_NAMES);
    assertThat(INPUT_NAMES).isEqualTo(EXPECTED_NATURAL_ORDER);
}

where the EXPECTED_NATURAL_ORDER is:

private static List<String> EXPECTED_NATURAL_ORDER = Arrays.asList("harry", "john", "ken", "mike", "usmon");

Some important points to note here are:

  • The list is sorted in ascending order according to the natural ordering of its elements
  • We can pass any Collection to sort method whose elements are Comparable (implement Comparable interface)
  • Here, we are passing a List of String class which is a Comparable class, and hence sorting works
  • Collection class changes the state of the List object being passed to sort. Therefore we don’t need to return the list

2.2. Sorting in Reverse Order

Let’s see how we can sort the same list in reverse alphabetical order.

Let’s use the sort method again but now provide a Comparator:

Comparator<String> reverseComparator = (first, second) -> second.compareTo(first);

Alternately we can simply use this static method from the Comparator interface:

Comparator<String> reverseComparator = Comparator.reverseOrder();

Once we have the reverse Comparator, we can simply pass it to sort:

@Test
void givenListOfStrings_whenUsingCollections_thenListIsSortedInReverse() {
    Comparator<String> reverseComparator = Comparator.reverseOrder();
    Collections.sort(INPUT_NAMES, reverseComparator); 
    assertThat(INPUT_NAMES).isEqualTo(EXPECTED_REVERSE_ORDER); 
}

where the EXPECTED_REVERSE_ORDER is:

private static List<String> EXPECTED_REVERSE_ORDER = Arrays.asList("usmon", "mike", "ken", "john", "harry");

A few relevant takeaways from this are:

  • since the String class is final, we cannot extend and override the compareTo method of the Comparable interface for reverse sorting
  • we can use the Comparator interface to implement a customized sorting strategy which is sorting in descending alphabetical order
  • since Comparator is a functional interface, we can use a lambda expression

3. Custom Sorting Using Comparator Interface

Often, we have to sort a list of Strings that need some custom sorting logic. That’s when we implement the Comparator interface and provide our desired sorting criteria.

3.1. Comparator to Sort List With Upper and Lower Case Strings

A typical scenario that can call for a custom sort can be a mixed list of Strings, starting with upper and lower cases.

Let’s set up and test this scenario:

@Test
void givenListOfStringsWithUpperAndLowerCaseMixed_whenCustomComparator_thenListIsSortedCorrectly() {
    List<String> movieNames = Arrays.asList("amazing SpiderMan", "Godzilla", "Sing", "Minions");
    List<String> naturalSortOrder = Arrays.asList("Godzilla", "Minions", "Sing", "amazing SpiderMan");
    List<String> comparatorSortOrder = Arrays.asList("amazing SpiderMan", "Godzilla", "Minions", "Sing");

    Collections.sort(movieNames);
    assertThat(movieNames).isEqualTo(naturalSortOrder);

    Collections.sort(movieNames, Comparator.comparing(s -> s.toLowerCase()));
    assertThat(movieNames).isEqualTo(comparatorSortOrder);
}

Note here:

  • the result of the sort before Comparator will be incorrect: [“Godzilla, “Minions” “Sing”, “amazing SpiderMan”]
  • String::toLowerCase is the key extractor that extracts a Comparable sort key from a type String and returns a Comparator<String>
  • the correct result after the sort with a custom Comparator will be: [“amazing SpiderMan”, “Godzilla”, “Minions”, “Sing”]

3.2. Comparator to Sort Special Characters

Let’s consider another example of a list with some names starting with a special character ‘@’.

We want them to be sorted at the end of the list, and the rest should be sorted in the natural order:

@Test
void givenListOfStringsIncludingSomeWithSpecialCharacter_whenCustomComparator_thenListIsSortedWithSpecialCharacterLast() {
    List<String> listWithSpecialCharacters = Arrays.asList("@laska", "blah", "jo", "@sk", "foo");

    List<String> sortedNaturalOrder = Arrays.asList("@laska", "@sk", "blah", "foo", "jo");
    List<String> sortedSpecialCharacterLast = Arrays.asList("blah", "foo", "jo", "@laska", "@sk");

    Collections.sort(listWithSpecialCharacters);
    assertThat(listWithSpecialCharacters).isEqualTo(sortedNaturalOrder);

    Comparator<String> specialSignComparator = Comparator.<String, Boolean>comparing(s -> s.startsWith("@"));
    Comparator<String> specialCharacterComparator = specialSignComparator.thenComparing(Comparator.naturalOrder());

    listWithSpecialCharacters.sort(specialCharacterComparator);
    assertThat(listWithSpecialCharacters).isEqualTo(sortedSpecialCharacterLast);
}

Finally, some key points are:

  • The output of the sort without the Comparator is: [“@alaska”, “@ask”, “blah”, “foo”, “jo”] which is not correct for our scenario
  • Like before, we have a key extractor that extracts a Comparable sort key from a type String and returns a specialCharacterLastComparator
  • We can chain the specialCharacterLastComparator do a secondary sort using thenComparing expecting another Comparator
  • Comparator.naturalOrder compares Comparable objects in the natural order.
  • final output after the Comparator in the sort is : [“blah”, “foo”, “jo”, “@alaska”, “@ask”]

4. Sorting Using Streams

Now, let’s use Java 8 Streams to sort the list of String.

4.1. Sorting in Natural Order

Let’s sort in the natural order first:

@Test
void givenListOfStrings_whenSortWithStreams_thenListIsSortedInNaturalOrder() {
    List<String> sortedList = INPUT_NAMES.stream()
      .sorted()
      .collect(Collectors.toList());

    assertThat(sortedList).isEqualTo(EXPECTED_NATURAL_ORDER);
}

Important, here the sorted method returns a stream of String sorted according to the natural order.

Additionally, elements of this Stream are Comparable.

4.2. Sorting in Reverse Order

Next, let’s pass a Comparator to sorted, which defines the  reverse sorting strategy:

@Test
void givenListOfStrings_whenSortWithStreamsUsingComparator_thenListIsSortedInReverseOrder() {
    List<String> sortedList = INPUT_NAMES.stream()
      .sorted((element1, element2) -> element2.compareTo(element1))
      .collect(Collectors.toList());
    assertThat(sortedList).isEqualTo(EXPECTED_REVERSE_ORDER);
}

Note, here we are using the Lamda function to define the Comparator

Alternately, we can get the same Comparator:

Comparator<String> reverseOrderComparator = Comparator.reverseOrder();

Then we’ll simply pass this reverseOrderComparator to sorted:

List<String> sortedList = INPUT_NAMES.stream()
  .sorted(reverseOrder)
  .collect(Collectors.toList());

5. Sorting Using TreeSet

TreeSet stores object in sorted and ascending order using a Comparable interface.

We can simply convert our unsorted list into TreeSet  and then collect it back as a List:

@Test
void givenNames_whenUsingTreeSet_thenListIsSorted() {
    SortedSet<String> sortedSet = new TreeSet<>(INPUT_NAMES);
    List<String> sortedList = new ArrayList<>(sortedSet);
    assertThat(sortedList).isEqualTo(EXPECTED_NATURAL_ORDER);
}

A constraint of this approach is that our original list shouldn’t have any duplicate values that it wants to preserve in the sorted list.

6. Sorting Using sort on List

We can also sort a list using the sort method of the List:

@Test
void givenListOfStrings_whenSortOnList_thenListIsSorted() {
    INPUT_NAMES.sort(Comparator.reverseOrder());
    assertThat(INPUT_NAMES).isEqualTo(EXPECTED_REVERSE_ORDER);
}

Note that the sort method sorts this list according to the order dictated by the specified Comparator.

7. Locale Sensitive List Sorting

Depending on the locale, the result of the alphabetical sorting can vary due to language rules.

Let’s take an example of a List of String:

 List<String> accentedStrings = Arrays.asList("único", "árbol", "cosas", "fútbol");

Let’s sort them as normal first:

 Collections.sort(accentedStrings);

The output would be: [“cosas”, “fútbol”, “árbol”, “único”].

However, we want them to be sorted using specific language rules.

Let’s create an instance of a Collator with the specific locale:

Collator esCollator = Collator.getInstance(new Locale("es"));

Note, here we created an instance of Collator using es locale.

We can then pass this Collator as a Comparator being used for sorting either on the list, Collection, or using Streams:

accentedStrings.sort((s1, s2) -> {
    return esCollator.compare(s1, s2);
});

The result of the sort would now be: [árbol, cosas, fútbol, único].

Finally, let’s show this all together:

@Test
void givenListOfStringsWithAccent_whenSortWithTheCollator_thenListIsSorted() {
    List<String> accentedStrings = Arrays.asList("único", "árbol", "cosas", "fútbol");
    List<String> sortedNaturalOrder = Arrays.asList("cosas", "fútbol", "árbol", "único");
    List<String> sortedLocaleSensitive = Arrays.asList("árbol", "cosas", "fútbol", "único");

    Collections.sort(accentedStrings);
    assertThat(accentedStrings).isEqualTo(sortedNaturalOrder);

    Collator esCollator = Collator.getInstance(new Locale("es"));

    accentedStrings.sort((s1, s2) -> {
        return esCollator.compare(s1, s2);
    });

    assertThat(accentedStrings).isEqualTo(sortedLocaleSensitive);
}

8. Sorting List With Accented Characters

Characters with accents or other adornments can be encoded in several different ways in Unicode and thus sorted differently.

We can either normalize the accented characters before sorting or can remove the accents from the characters.

Let’s look into both of these ways of sorting accented lists.

8.1. Normalize Accented List and Sort

To sort accurately such a list of Strings, let’s normalize accented characters using java.text.Normalizer

Let’s start with a list of accented Strings:

List<String> accentedStrings = Arrays.asList("único","árbol", "cosas", "fútbol");

Next, let’s define a Comparator with a Normalize function and pass it to our sort method:

Collections.sort(accentedStrings, (o1, o2) -> {
    o1 = Normalizer.normalize(o1, Normalizer.Form.NFD);
    o2 = Normalizer.normalize(o2, Normalizer.Form.NFD);
    return o1.compareTo(o2);
});

The sorted list after normalization will be: [árbol, cosas, fútbol, único]

Importantly, here we are normalizing data using the form Normalizer.Form.NFD uses Canonical decomposition for accented characters. There are a few other forms that can also be used for normalization.

8.2. Strip Accents and Sort

Let’s use the StringUtils stripAccents method in the Comparator and pass it to the sort method:

@Test
void givenListOfStrinsWithAccent_whenComparatorWithNormalizer_thenListIsNormalizedAndSorted() {
    List<String> accentedStrings = Arrays.asList("único", "árbol", "cosas", "fútbol");

    List<String> naturalOrderSorted = Arrays.asList("cosas", "fútbol", "árbol", "único");
    List<String> stripAccentSorted = Arrays.asList("árbol","cosas", "fútbol","único");

    Collections.sort(accentedStrings);
    assertThat(accentedStrings).isEqualTo(naturalOrderSorted);
    Collections.sort(accentedStrings, Comparator.comparing(input -> StringUtils.stripAccents(input)));
    assertThat(accentedStrings).isEqualTostripAccentSorted); 
}

9. Sorting Using Rule-Based Collator

We can also define our custom rules to sort alphabetically using java text.RuleBasedCollator.

Here, let’s define our custom sorting rule and pass it to RuleBasedCollator:

@Test
void givenListofStrings_whenUsingRuleBasedCollator_then ListIsSortedUsingRuleBasedCollator() throws ParseException {
    List<String> movieNames = Arrays.asList(
      "Godzilla","AmazingSpiderMan","Smurfs", "Minions");

    List<String> naturalOrderExpected = Arrays.asList(
      "AmazingSpiderMan", "Godzilla", "Minions", "Smurfs");
    Collections.sort(movieNames);

    List<String> rulesBasedExpected = Arrays.asList(
      "Smurfs", "Minions", "AmazingSpiderMan", "Godzilla");

    assertThat(movieNames).isEqualTo(naturalOrderExpected);

    String rule = "< s, S < m, M < a, A < g, G";

    RuleBasedCollator rulesCollator = new RuleBasedCollator(rule);
    movieNames.sort(rulesCollator);

    assertThat(movieNames).isEqualTo(rulesBasedExpected);
}

Some of the important points to consider are:

  • RuleBasedCollator maps characters to sort keys
  • the rule defined above is of form <relations> but there are other forms too in rules
  • character s is smaller than m, and M is smaller than a, where A is smaller than g as per the rule definition
  • a format exception will be thrown if the build process of the rules fails
  • RuleBasedCollator implements the Comparator interface, hence can be passed to sort

10. Conclusion

In this article, we looked into various techniques for sorting lists alphabetically in java.

First, we used Collections, then we explained the Comparator interface with some common examples.

After Comparator, we looked into the sort method of the List followed by TreeSet.

We also explored how we can handle sorting the list of String in a different locale, normalizing and sorting accented lists, as well as the use of RuleBasedCollator with custom rules

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.

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