概述

本教程将讨论如何使用Java内置类、第三方库以及自定义实现创建一个表示Map(/java-map-entry)中键值关联的Entry对象。

2. 使用Java内置类

Java为Map提供了两个简单的Entry接口实现,让我们来了解一下。

2.1. 使用AbstractMap.SimpleEntry

SimpleEntryAbstractMap类中的静态嵌套类。它提供了两种不同的构造函数来初始化实例:

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

如图所示,其中一个构造函数接受键和值,而另一个构造函数接受一个Entry实例来初始化新的Entry实例。

2.2. 使用AbstractMap.SimpleImmutableEntry

SimpleEntry类似,我们可以使用SimpleImmutableEntry创建Entry

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

SimpleEntry不同的是,SimpleImmutableEntry在初始化Entry实例后不允许更改值。如果尝试更改值,它会抛出java.lang.UnsupportedOperationException

2.3. 使用Map.entry()

从Java 9版本开始,Map接口有一个静态方法entry()用于创建Entry

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

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

需要注意的是,通过这种方式创建的Entry也是不可变的,如果在初始化后尝试更改值,也会抛出java.lang.UnsupportedOperationException

3. 第三方库

除了Java本身,还有一些流行库提供了创建Entry的便捷方式。

3.1. 使用Apache Commons Collections4库

首先,我们需要添加Maven依赖:

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

需要说明的是,除了Entry接口外,库还提供了一个名为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"));

DefaultMapEntry类提供了三种不同的构造函数。第一个接受键值对,第二个和第三个分别接受EntryKeyValue类型的参数。

UnmodifiableMapEntry类的行为与此相同:

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

然而,从其名称可以理解,UnmodifiableMapEntry也不允许在初始化后更改值。**

3.2. 使用Google Guava库

首先,我们添加Maven依赖:

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

现在,让我们看看如何使用immutableEntry()方法:

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

由于它创建了不可变的Entry,如果尝试更改值,它会抛出java.lang.UnsupportedOperationException

4. 自定义实现

到目前为止,我们已经看到了几种创建Entry实例的方法,表示键值关联。这些类的设计方式确保它们遵循Map接口实现(如HashMap / java-hashmap)的内部逻辑。

这意味着只要我们遵循相同的规则,就可以创建自己的Entry接口实现。首先,让我们添加一个简单的实现:

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
}

最后,让我们看一些用法示例:

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. 总结

在这篇文章中,我们学习了如何利用Java提供的现有选项以及一些流行的第三方库提供的替代方案来创建Entry实例。此外,我们还创建了一个自定义实现,并展示了几个用法例子。

如往常一样,这些示例的代码可在GitHub上找到。