2. Maven 依赖配置
首先需要在 pom.xml
中添加 Maven 依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>
✅ 最新版本可在 Maven 中央仓库 查询
3. OrderedMap 核心特性
实现了 OrderedMap
接口的映射容器具有以下关键特性:
- 保持键的插入顺序(注意:不是排序顺序)
- 支持双向遍历:
- 正向:
firstKey()
+nextKey()
- 反向:
lastKey()
+previousKey()
- 正向:
- 专用迭代器:可通过
MapIterator
遍历 - 增强操作方法:支持查找/修改/删除/替换元素
⚠️ 特别注意:键集(key set)保持的是插入顺序而非自然排序顺序
4. OrderedMap 实战演示
我们使用测试类演示 LinkedMap
(OrderedMap
的实现类)的用法。先准备数据:
public class OrderMapUnitTest {
private String[] names = {"Emily", "Mathew", "Rose", "John", "Anna"};
private Integer[] ages = {37, 28, 40, 36, 21};
private LinkedMap<String, Integer> runnersLinkedMap;
//...
}
初始化映射:
@Before
public void createRunners() {
this.runnersLinkedMap = new LinkedMap<>();
for (int i = 0; i < RUNNERS_COUNT; i++) {
runners.put(this.names[i], this.ages[i]);
}
}
4.1 正向遍历
使用正向迭代器遍历:
@Test
public void givenALinkedMap_whenIteratedForwards_thenPreservesOrder() {
String name = this.runnersLinkedMap.firstKey();
int i = 0;
while (name != null) {
assertEquals(name, names[i]);
name = this.runnersLinkedMap.nextKey(name);
i++;
}
}
⚠️ 当到达最后一个键时,nextKey()
返回 null
4.2 反向遍历
从最后一个键开始反向遍历:
@Test
public void givenALinkedMap_whenIteratedBackwards_thenPreservesOrder() {
String name = this.runnersLinkedMap.lastKey();
int i = RUNNERS_COUNT - 1;
while (name != null) {
assertEquals(name, this.names[i]);
name = this.runnersLinkedMap.previousKey(name);
i--;
}
}
⚠️ 到达第一个键时,previousKey()
返回 null
4.3 MapIterator 使用示例
通过 mapIterator()
获取专用迭代器:
@Test
public void givenALinkedMap_whenIteratedWithMapIterator_thenPreservesOrder() {
OrderedMapIterator<String, Integer> runnersIterator
= this.runnersLinkedMap.mapIterator();
int i = 0;
while (runnersIterator.hasNext()) {
runnersIterator.next();
assertEquals(runnersIterator.getKey(), this.names[i]);
assertEquals(runnersIterator.getValue(), this.ages[i]);
i++;
}
}
✅ 迭代器严格保持键值对的插入顺序
4.4 元素移除操作
支持通过键或索引移除元素:
@Test
public void givenALinkedMap_whenElementRemoved_thenSizeDecrease() {
LinkedMap<String, Integer> lmap
= (LinkedMap<String, Integer>) this.runnersLinkedMap;
Integer johnAge = lmap.remove("John"); // 按键移除
assertEquals(johnAge, new Integer(36));
assertEquals(lmap.size(), RUNNERS_COUNT - 1);
Integer emilyAge = lmap.remove(0); // 按索引移除
assertEquals(emilyAge, new Integer(37));
assertEquals(lmap.size(), RUNNERS_COUNT - 2);
}
5. 核心实现类
当前版本(4.1)提供两个实现类:
ListOrderedMap
- 内部使用
java.util.List
维护键顺序 - 装饰器模式实现,通过静态方法创建:
ListOrderedMap.decorate(Map map)
LinkedMap
- 基于
HashMap
扩展 - 原生支持双向迭代等
OrderedMap
特性
增强方法(超出 OrderedMap 接口)
两个实现类均提供以下实用方法:
方法 | 说明 |
---|---|
asList() |
获取有序的键列表 List<K> |
get(int index) |
按索引获取元素(区别于 get(Object o) ) |
indexOf(Object o) |
获取对象在有序映射中的索引 |
asList() 使用示例
@Test
public void givenALinkedMap_whenConvertedToList_thenMatchesKeySet() {
LinkedMap<String, Integer> lmap
= (LinkedMap<String, Integer>) this.runnersLinkedMap;
List<String> listKeys = new ArrayList<>();
listKeys.addAll(this.runnersLinkedMap.keySet());
List<String> linkedMap = lmap.asList();
assertEquals(listKeys, linkedMap);
}
索引操作示例
@Test
public void givenALinkedMap_whenSearchByIndexIsUsed_thenMatchesConstantArray() {
LinkedMap<String, Integer> lmap
= (LinkedMap<String, Integer>) this.runnersLinkedMap;
for (int i = 0; i < RUNNERS_COUNT; i++) {
String name = lmap.get(i); // 按索引获取
assertEquals(name, this.names[i]);
assertEquals(lmap.indexOf(this.names[i]), i); // 验证索引
}
}
6. 总结
本文系统梳理了 OrderedMap
接口的核心特性与实现方案:
- 核心价值:在保留 Map 功能的同时,提供可预测的键顺序
- 遍历能力:支持正向/反向迭代及专用
MapIterator
- 实现选择:
-
LinkedMap
:轻量级原生实现 -
ListOrderedMap
:装饰器模式实现
-
- 增强操作:索引访问和列表转换等实用方法
📖 深入参考:Apache Commons Collections 官方文档
💻 完整示例代码:GitHub 项目