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 – Summer Sale 2026 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

Course – Summer Sale 2026 – NPI (cat=Baeldung)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

1. Overview

Morse code encodes text characters using sequences of dots and dashes to represent letters, numbers, and punctuation. Samuel Morse and Alfred Vail developed it in the early 1830s for telegraphy use.

In this tutorial, we’ll write a method that translates from English to Morse code. Then, we’ll write the method which does the opposite.

2. Writing Morse Code

Let’s understand Morse code and its alphabet.

2.1. What Is the Morse Code?

In Morse code, each letter is represented by a unique combination of short signals (dots) and long signals (dashes), allowing for communication through a series of on-off signals. According to the common usage, we’ll represent dots with “.” and dashes with ““. Those two characters are enough to write the whole Morse alphabet.

However, we’ll need something more to write sentences. As Morse indeed targeted non-written communication, the flow is essential to decrypt a Morse message. For this reason, the operator responsible for transmitting a Morse message would leave a short pause between each letter. Additionally, he would leave a longer pause between each word. As a result, a representation that wouldn’t take those pauses into account wouldn’t allow for decoding.

A common choice is to leave a blank space ” ” to represent the pause between each word. We’ll also use a “/” to codify the space character between two words. As the slash is also a character, blank spaces will surround it like the others.

2.2. Bidirectional Mapping Between English and Morse

To translate easily from English to Morse and reversely, we’d like to have a bidirectional mapping between both alphabets. Thus, we’ll use Apache Commons Collection’s BidiMap data structure. It’s a Map that allows access by key or by value. This way, we’ll use it for both translating methods. However, if we’re interested in translating only one way, we’d directly use a Map.

First, let’s include the latest version of the library in our pom.xml:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.5.0-M2</version>
</dependency>

We can now create our mapping and initialize it in a static block:

public class MorseTranslator {
    
    private static final BidiMap<String, String> morseAlphabet = new DualHashBidiMap<>();
    
    static {
        morseAlphabet.put("A", ".-");
        morseAlphabet.put("B", "-...");
        morseAlphabet.put("C", "-.-.");
        morseAlphabet.put("D", "-..");
        morseAlphabet.put("E", ".");
        morseAlphabet.put("F", "..-.");
        morseAlphabet.put("G", "--.");
        morseAlphabet.put("H", "....");
        morseAlphabet.put("I", "..");
        // etc
        morseAlphabet.put(" ", "/");
    }
    
}

Let’s note that we added the translation for the blank character. Furthermore, we restrict ourselves to letters, numbers, and punctuation characters. If we want to use also accented characters, we’d need to use another data structure or make choices because various accented characters can match the same Morse code. For instance, “à” and “å” both correspond to “.–.-” in Morse.

3. Translating English Into Morse

First, let’s write a method to translate an English sentence into Morse code.

3.1. General Algorithm

Our BidiMap contains only capital letters because capitalization doesn’t change the translation. Thus, we’ll start with capitalizing the word. Then, we’ll iterate over the letters and translate them one by one:

static String englishToMorse(String english) {
    String upperCaseEnglish = english.toUpperCase();
    String[] morse = new String[upperCaseEnglish.length()];
    for (int index = 0; index < upperCaseEnglish.length(); index++) {
        String morseCharacter = morseAlphabet.get(String.valueOf(upperCaseEnglish.charAt(index)));
        morse[index] = morseCharacter;
    }
    return String.join(" ", morse);
}

It’s convenient to store the translations into an array of Morse Strings. This intermediate array has as many values as the number of characters in the input. In the end, we use the String.join() method to concatenate all entries, using a blank space as a delimiter.

We can now test our method. Since we’d like to check that capitalization doesn’t matter, we’ll write a parameterized test with various inputs expecting the same output:

@ParameterizedTest
@ValueSource(strings = {"MORSE CODE!", "morse code!", "mOrSe cOdE!"})
void givenAValidEnglishWordWhateverTheCapitalization_whenEnglishToMorse_thenTranslatedToMorse(String english) {
    assertEquals("-- --- .-. ... . / -.-. --- -.. . -.-.-----.", MorseTranslator.englishToMorse(english));
}

In addition, we can note that the space between the two words translates to “ / ” as expected.

3.2. Edge Cases

For the moment, our program doesn’t take into account potentially malformed inputs. However, we’d like to refuse sentences that contain invalid characters. In such cases, we’ll throw an IllegalArgumentException:

String morseCharacter = morseAlphabet.get(String.valueOf(upperCaseEnglish.charAt(index)));
if (morseCharacter == null) {
    throw new IllegalArgumentException("Character " + upperCaseEnglish.charAt(index) + " can't be translated to morse");
}
morse[index] = morseCharacter;

The modification is pretty straightforward because if a character is invalid, it isn’t present as a key of the bidirectional map. Hence, the get() method returns null. We can also add a null safety check on top of our method. In a nutshell, our final method reads:

static String englishToMorse(String english) {
    if (english == null) {
        return null;
    }
    String upperCaseEnglish = english.toUpperCase();
    String[] morse = new String[upperCaseEnglish.length()];
    for (int index = 0; index < upperCaseEnglish.length(); index++) {
        String morseCharacter = morseAlphabet.get(String.valueOf(upperCaseEnglish.charAt(index)));
        if (morseCharacter == null) {
            throw new IllegalArgumentException("Character " + upperCaseEnglish.charAt(index) + " can't be translated to morse");
        }
        morse[index] = morseCharacter;
    }
    return String.join(" ", morse);
}

Lastly, we can add a unit test with a non-translatable sentence:

@Test
void givenAnEnglishWordWithAnIllegalCharacter_whenEnglishToMorse_thenThrows() {
    String english = "~This sentence starts with an illegal character";
    assertThrows(IllegalArgumentException.class, () -> MorseTranslator.englishToMorse(english));
}

4. Translating Morse Into English

Let’s now write the reverse method. Once again, we’ll focus on the big picture before diving into edge cases.

4.1. General Algorithm

The concept is the same: for each Morse character, we find the English translation in the BidiMap. The getKey() method allows us to do that. Then, we need to iterate over every Morse character:

static String morseToEnglish(String morse) {
    String[] morseUnitCharacters = morse.split(" ");
    StringBuilder stringBuilder = new StringBuilder();
    for (int index = 0; index < morseUnitCharacters.length; index ++) {
        String englishCharacter = morseAlphabet.getKey(morseUnitCharacters[index]);
        stringBuilder.append(englishCharacter);
    }
    return stringBuilder.toString();
}

We isolated every Morse character thanks to the String.split() method. Appending every English translation to a StringBuilder is the most efficient way to concatenate the result.

Let’s now verify that our method returns the correct result:

@Test
void givenAValidMorseWord_whenMorseToEnglish_thenTranslatedToUpperCaseEnglish() {
    assertEquals("MORSE CODE!", MorseTranslator.morseToEnglish("-- --- .-. ... . / -.-. --- -.. . -.-.-----."));
}

Finally, we can recall that the output will always be in capital letters.

4.2. Edge Cases

Additionally, we want to refuse inputs containing invalid Morse characters. Like in englishToMorse(), we’ll throw an IllegalArgumentException in this case. Moreover, we can also handle the specific case of a null input. Here, we also have to deal with an empty input separately because of the internal functioning of the split() method.

To recap, let’s write our final method:

static String morseToEnglish(String morse) {
    if (morse == null) {
        return null;
    }
    if (morse.isEmpty()) {
        return "";
    }
    String[] morseUnitCharacters = morse.split(" ");
    StringBuilder stringBuilder = new StringBuilder();
    for (int index = 0; index < morseUnitCharacters.length; index ++) {
        String englishCharacter = morseAlphabet.getKey(morseUnitCharacters[index]);
        if (englishCharacter == null) {
            throw new IllegalArgumentException("Character " + morseUnitCharacters[index] + " is not a valid morse character");
        }
        stringBuilder.append(englishCharacter);
    }
    return stringBuilder.toString();
}

Dealing with invalid characters was as straightforward as in the previous case because if a Morse code doesn’t match any of the BidiMap‘s values, the getKey() method returns null.

Lastly, we can also test the error case:

@Test
void givenAMorseWordWithAnIllegalCharacter_whenMorseToEnglish_thenThrows() {
    assertThrows(IllegalArgumentException.class, () -> MorseTranslator.morseToEnglish(".!!!!!!!"));
}

5. Conclusion

In this article, we learned about the Morse code and wrote a simple two-way translator between Morse and English. Most considerations aren’t specific to Morse, so we could probably make our code more generic to deal with any language that can define a bidirectional mapping with English.

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 – Summer Sale 2026 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

Course – Summer Sale 2026 – NPI (All)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

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