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

We often make use of maps in our programs, as a means to associate keys with values. Typically in our Java programs, especially since the introduction of generics, we will have all of the keys be the same type and all of the values be the same type. For example, a map of IDs to values in a data store.

On some occasions, we might want to use a map where the keys are not always the same type. For example, if we change our ID types from Long to String, then our data store will need to support both key types – Long for the old entries and String for the new ones.

Unfortunately, the Java Map interface doesn’t allow for multiple key types, so we need to find another solution. We’re going to explore a few ways this can be achieved in this article.

2. Using Generic Supertypes

The easiest way to achieve this is to have a map where the key type is the closest supertype to all of our keys. In some cases, this might be easy – for example, if our keys are Long and Double then the closest supertype is Number:

Map<Number, User> users = new HashMap<>();

users.get(longId);
users.get(doubleId);

However, in other cases, the closest supertype is Object. This has the downside that it completely removes type safety from our map:

Map<Object, User> users = new HashMap<>();

users.get(longId); /// Works.
users.get(stringId); // Works.
users.get(Instant.now()); // Also works.

In this case, the compiler doesn’t stop us from passing the wrong types in, effectively removing all type safety from our map. In some cases, this might be fine. For example, this will probably be fine if another class encapsulates the map so as to enforce the type safety itself.

However, it still opens up risks in how the map can be used.

3. Multiple Maps

If type safety is important, and we’ll be encapsulating our map inside another class, another simple option is to have multiple maps. In this case, we’d have a different map for each of our supported keys:

Map<Long, User> usersByLong = new HashMap<>();
Map<String, User> usersByString = new HashMap<>();

Doing this ensures that the compiler will keep type safety for us. If we try to use an Instant here, then the compiler won’t let us, so we’re safe here.

Unfortunately, this adds complexity because we need to know which of our maps to use. This means that we either have different methods working with different maps, or else we’re doing type checking everywhere.

This also doesn’t scale well. We will need to add a new map and new checks all over if we ever need to add a new key type. For two or three key types, this is manageable, but it quickly gets to be too much.

4. Key Wrapper Types

If we need to have type safety, and we don’t want the maintainability burden of many maps, then we need to find a way to have a single map that can have different values in the key. This means that we need to find some way to have a single type that is actually different types. We can achieve this in two different ways – with a single wrapper or with an interface and subclasses.

4.1. Single Wrapper Class

One option we have is to write a single class that can wrap any of our possible key types. This will have a single field for the actual key value, correct equals and hashCode methods, and then one constructor for each possible type:

class MultiKeyWrapper {
    private final Object key;

    MultiKeyWrapper(Long key) {
        this.key = key;
    }

    MultiKeyWrapper(String key) {
        this.key = key;
    }

    @Override
    public bool equals(Object other) { ... }

    @Override
    public int hashCode() { ... }
}

This is guaranteed to be typesafe because it can only be constructed with either a Long or a String. And we can use it as a single type in our map because it is in itself a single class:

Map<MultiKeyWrapper, User> users = new HashMap<>();
users.get(new MultiKeyWrapper(longId)); // Works
users.get(new MultiKeyWrapper(stringId)); // Works
users.get(new MultiKeyWrapper(Instant.now())); // Compilation error

We simply need to wrap our Long or String in our new MultiKeyWrapper for every access to the map.

This is relatively simple, but it will make extension slightly harder. Whenever we want to support any additional types then, we’ll need to change our MultiKeyWrapper class to support it.

4.2. Interface and Subclasses

Another alternative is to write an interface to represent our key wrapper and then write an implementation of this interface for every type that we want to support:

interface MultiKeyWrapper {}

record LongMultiKeyWrapper(Long value) implements MultiKeyWrapper {}
record StringMultiKeyWrapper(String value) implements MultiKeyWrapper {}

As we can see, these implementations can use the Record functionality introduced in Java 14, which will make the implementation much easier.

As before, we can then use our MultiKeyWrapper as the single key type for a map. We then use the appropriate implementation for the key type that we want to use:

Map<MultiKeyWrapper, User> users = new HashMap<>();
users.get(new LongMultiKeyWrapper(longId)); // Works
users.get(new StringMultiKeyWrapper(stringId)); // Works

In this case, we don’t have a type to use for anything else, so we can’t even write invalid code in the first place.

With this solution, we support additional key types not by changing the existing classes but by writing a new one. This is easier to support, but it also means that we have less control over what key types are supported.

However, this can be managed by the correct use of visibility modifiers. Classes can only implement our interface if they have access to it, so if we make it package-private, then only classes in the same package can implement it.

5. Conclusion

Here we’ve seen some ways to represent a map of keys to values, but where the keys are not always of the same type.

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)