Generic Top

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

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we'll discuss how to use Java's built-in classes, third-party libraries, and our custom implementation to create an Entry object that represents a key-value association in a Map.

2. Using Java Built-in Classes

Java provides the Map.Entry interface with two simple implementations to create an Entry. Let's take a look at them.

2.1. Using AbstractMap.SimpleEntry

The SimpleEntry class is a static nested class in the AbstractMap class. It provides two different constructors to initialize an instance:

AbstractMap.SimpleEntry<String, String> firstEntry = new AbstractMap.SimpleEntry<>("key1", "value1");
AbstractMap.SimpleEntry<String, String> secondEntry = new AbstractMap.SimpleEntry<>("key2", "value2");
AbstractMap.SimpleEntry<String, String> thirdEntry = new AbstractMap.SimpleEntry<>(firstEntry);
thirdEntry.setValue("a different value");

assertThat(Stream.of(firstEntry, secondEntry, thirdEntry))
  .extracting("key", "value")
  .containsExactly(
    tuple("key1", "value1"),
    tuple("key2", "value2"),
    tuple("key1", "a different value"));

As we can see here, one of the constructors accepts the key and the value, while the other one accepts an Entry instance to initialize a new Entry instance.

2.2. Using AbstractMap.SimpleImmutableEntry

Just like with the SimpleEntry, we can use SimpleImmutableEntry to create entries:

AbstractMap.SimpleImmutableEntry<String, String> firstEntry = new AbstractMap.SimpleImmutableEntry<>("key1", "value1");
AbstractMap.SimpleImmutableEntry<String, String> secondEntry = new AbstractMap.SimpleImmutableEntry<>("key2", "value2");
AbstractMap.SimpleImmutableEntry<String, String> thirdEntry = new AbstractMap.SimpleImmutableEntry<>(firstEntry);

assertThat(Stream.of(firstEntry, secondEntry, thirdEntry))
  .extracting("key", "value")
  .containsExactly(
    tuple("key1", "value1"),
    tuple("key2", "value2"),
    tuple("key1", "value1"));

In contrast to SimpleEntry, SimpleImmutableEntry doesn't allow us to change the value after initializing the Entry instance. If we try to change the value, it throws java.lang.UnsupportedOperationException.

2.3. Using Map.entry

Since version 9, Java has a static method entry() in the Map interface to create an Entry:

Map.Entry<String, String> entry = Map.entry("key", "value");

assertThat(entry.getKey()).isEqualTo("key");
assertThat(entry.getValue()).isEqualTo("value");

We need to keep in mind that the entry created this way is also immutable and would lead to a java.lang.UnsupportedOperationException if we try to change the value after the initialization.

3. Third-Party Libraries

Besides Java itself, there are a few popular libraries that provide nice ways to create entries.

3.1. Using Apache commons-collections4 Library

Let's start by including our Maven dependency first:

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

We should mention that besides the Entry interface, the library also provides an interface called KeyValue:

Map.Entry<String, String> firstEntry = new DefaultMapEntry<>("key1", "value1");
KeyValue<String, String> secondEntry = new DefaultMapEntry<>("key2", "value2");

KeyValue<String, String> thirdEntry = new DefaultMapEntry<>(firstEntry);
KeyValue<String, String> fourthEntry = new DefaultMapEntry<>(secondEntry);

firstEntry.setValue("a different value");

assertThat(firstEntry)
  .extracting("key", "value")
  .containsExactly("key1", "a different value");

assertThat(Stream.of(secondEntry, thirdEntry, fourthEntry))
  .extracting("key", "value")
  .containsExactly(
    tuple("key2", "value2"),
    tuple("key1", "value1"),
    tuple("key2", "value2"));

The DefaultMapEntry class provides three different constructors. While the first one accepts the key-value pair, the second and the third accept the parameter type of Entry and KeyValue, respectively.

The UnmodifiableMapEntry class also behaves the same way:

Map.Entry<String, String> firstEntry = new UnmodifiableMapEntry<>("key1", "value1");
KeyValue<String, String> secondEntry = new UnmodifiableMapEntry<>("key2", "value2");

KeyValue<String, String> thirdEntry = new UnmodifiableMapEntry<>(firstEntry);
KeyValue<String, String> fourthEntry = new UnmodifiableMapEntry<>(secondEntry);

assertThat(firstEntry)
  .extracting("key", "value")
  .containsExactly("key1", "value1");

assertThat(Stream.of(secondEntry, thirdEntry, fourthEntry))
  .extracting("key", "value")
  .containsExactly(
    tuple("key2", "value2"),
    tuple("key1", "value1"),
    tuple("key2", "value2"));

However, as we can understand from its name, UnmodifiableMapEntry also doesn't allow us to change the value after the initialization.

3.2. Using Google Guava Library

Let's first include our Maven dependency:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
 </dependency>

Now, let's see how we can use the immutableEntry() method:

Map.Entry<String, String> firstEntry = Maps.immutableEntry("key1", "value1");
Map.Entry<String, String> secondEntry = Maps.immutableEntry("key2", "value2");

assertThat(Stream.of(firstEntry, secondEntry))
  .extracting("key", "value")
  .containsExactly(
    tuple("key1", "value1"),
    tuple("key2", "value2"));

Since it creates an immutable entry, if we try to change the value, it throws java.lang.UnsupportedOperationException.

4. Custom Implementation

So far, we have seen a few options to create an Entry instance to represent a key-value association. These classes have been designed in a way that they must comply with the internal logic of the Map interface implementations such as HashMap

This means that as long as we comply with the same, we can create our own implementation of the Entry interface. First, let's add a simple implementation:

public class SimpleCustomKeyValue<K, V> implements Map.Entry<K, V> {

    private final K key;
    private V value;

    public SimpleCustomKeyValue(K key, V value) {
        this.key = key;
        this.value = value;
    }
    // standard getters and setters
    // standard equals and hashcode
    // standard toString
}

Finally, let's see a few examples of usage:

Map.Entry<String, String> firstEntry = new SimpleCustomKeyValue<>("key1", "value1");

Map.Entry<String, String> secondEntry = new SimpleCustomKeyValue<>("key2", "value2");
secondEntry.setValue("different value");

Map<String, String> map = Map.ofEntries(firstEntry, secondEntry);

assertThat(map)
  .isEqualTo(ImmutableMap.<String, String>builder()
    .put("key1", "value1")
    .put("key2", "different value")
    .build());

5. Conclusion

In this article, we learned how we could use the existing options that Java gives and a few alternatives that some popular third-party libraries provide to create an Entry instance. Additionally, we also created a custom implementation and showed a few examples of usage.

As always, the code for these examples is available over on GitHub.

Generic bottom

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

>> CHECK OUT THE COURSE
Generic footer banner
2 Comments
Oldest
Newest
Inline Feedbacks
View all comments
Comments are closed on this article!