Java SortedMap 接口详解:从入门到实战

1. 引言

在 Java 集合框架中,Map 是最常用的接口之一,用于存储键值对。然而,标准的 HashMap 并不保证键的迭代顺序。当我们需要按照键的自然顺序或自定义规则进行排序遍历时,SortedMap 接口便派上了用场。

SortedMap 是 Map 接口的子接口,它最重要的特性是保证键按照升序排列 。本文将深入剖析 SortedMap 的核心方法、实现类(如 TreeMap)、与 HashMap 的对比,并通过丰富的代码示例帮助你快速上手。

2. SortedMap 接口概述

2.1 什么是 SortedMap

SortedMap 位于 java.util 包中,继承自 Map 接口。它扩展了 Map 的语义,规定其中的键必须按照自然顺序 (如 Integer 从小到大、String 按字典序)或构造时指定的比较器(Comparator) 进行排序。

java 复制代码
public interface SortedMap<K, V> extends Map<K, V> {
    // 核心方法
    Comparator<? super K> comparator();
    SortedMap<K, V> subMap(K fromKey, K toKey);
    SortedMap<K, V> headMap(K toKey);
    SortedMap<K, V> tailMap(K fromKey);
    K firstKey();
    K lastKey();
}

2.2 与 Map 的关系

SortedMap 在 Map 的基础上增加了对顺序 的约束。所有实现 SortedMap 的类(最典型的是 TreeMap)在遍历时,键都是有序的。

3. SortedMap 的核心方法

3.1 获取首尾键:firstKey() 与 lastKey()

这两个方法分别返回当前映射中的最小键和最大键。如果映射为空,会抛出 NoSuchElementException。

java 复制代码
import java.util.SortedMap;
import java.util.TreeMap;

public class FirstLastKeyExample {
    public static void main(String[] args) {
        SortedMap<Integer, String> scores = new TreeMap<>();
        scores.put(85, "张三");
        scores.put(92, "李四");
        scores.put(76, "王五");

        System.out.println("最小键: " + scores.firstKey()); // 76
        System.out.println("最大键: " + scores.lastKey());  // 92
    }
}

3.2 视图截取:subMap()、headMap()、tailMap()

这三个方法是 SortedMap 最强大的功能,用于获取原映射的子视图:

  • subMap(fromKey, toKey):返回键从 fromKey(包含)到 toKey(不包含)的部分。
  • headMap(toKey):返回键严格小于 toKey 的部分。
  • tailMap(fromKey):返回键大于等于 fromKey 的部分。
java 复制代码
import java.util.SortedMap;
import java.util.TreeMap;

public class SubMapExample {
    public static void main(String[] args) {
        SortedMap<Integer, String> map = new TreeMap<>();
        map.put(1, "一");
        map.put(2, "二");
        map.put(3, "三");
        map.put(4, "四");
        map.put(5, "五");

        SortedMap<Integer, String> sub = map.subMap(2, 5);
        System.out.println("subMap(2,5): " + sub); // {2=二, 3=三, 4=四}

        SortedMap<Integer, String> head = map.headMap(3);
        System.out.println("headMap(3): " + head); // {1=一, 2=二}

        SortedMap<Integer, String> tail = map.tailMap(3);
        System.out.println("tailMap(3): " + tail); // {3=三, 4=四, 5=五}
    }
}

注意 :这三个方法返回的是视图(View),而非副本。对子视图的修改会直接反映到原映射中。

3.3 获取比较器:comparator()

返回当前映射使用的比较器。如果使用键的自然顺序,则返回 null。

java 复制代码
import java.util.Comparator;
import java.util.SortedMap;
import java.util.TreeMap;

public class ComparatorExample {
    public static void main(String[] args) {
        // 使用自然顺序
        SortedMap<Integer, String> natural = new TreeMap<>();
        System.out.println("自然顺序 comparator: " + natural.comparator()); // null

        // 使用自定义比较器(降序)
        SortedMap<Integer, String> custom = new TreeMap<>(Comparator.reverseOrder());
        custom.put(1, "一");
        custom.put(2, "二");
        System.out.println("自定义 comparator: " + custom.comparator());
        System.out.println("降序结果: " + custom); // {2=二, 1=一}
    }
}

4. SortedMap 的主要实现类:TreeMap

4.1 TreeMap 简介

TreeMap 是 SortedMap 最常用的实现类,底层基于红黑树(Red-Black Tree) 数据结构。它保证了 containsKey、get、put、remove 等操作的时间复杂度为 O(log n)。

java 复制代码
import java.util.SortedMap;
import java.util.TreeMap;

public class TreeMapExample {
    public static void main(String[] args) {
        SortedMap<String, Integer> fruitPrices = new TreeMap<>();
        fruitPrices.put("香蕉", 3);
        fruitPrices.put("苹果", 5);
        fruitPrices.put("橙子", 4);

        // 按键的自然顺序(字典序)遍历
        for (String key : fruitPrices.keySet()) {
            System.out.println(key + " -> " + fruitPrices.get(key));
        }
        // 输出顺序:橙子、苹果、香蕉(按拼音/字典序)
    }
}

4.2 自定义排序规则

当键的自然顺序不符合需求时,可以在构造 TreeMap 时传入 Comparator。

java 复制代码
import java.util.Comparator;
import java.util.SortedMap;
import java.util.TreeMap;

public class CustomSortExample {
    public static void main(String[] args) {
        // 按字符串长度排序
        SortedMap<String, String> map = new TreeMap<>(Comparator.comparingInt(String::length));

        map.put("Java", "语言");
        map.put("Python", "语言");
        map.put("C", "语言");

        System.out.println(map.keySet()); // [C, Java, Python](按长度升序)
    }
}

5. SortedMap 与 HashMap 的对比

特性 SortedMap(TreeMap) HashMap
键的顺序 有序(自然顺序或 Comparator) 无序
时间复杂度 O(log n) O(1)(平均)
是否允许 null 键 不允许(TreeMap 会抛 NPE) 允许(仅一个)
底层结构 红黑树 哈希表 + 链表/红黑树
适用场景 需要排序遍历、范围查询 快速存取、不关心顺序
java 复制代码
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

public class CompareExample {
    public static void main(String[] args) {
        Map<Integer, String> hashMap = new HashMap<>();
        hashMap.put(3, "三");
        hashMap.put(1, "一");
        hashMap.put(2, "二");
        System.out.println("HashMap 遍历顺序: " + hashMap.keySet()); // 无序

        Map<Integer, String> treeMap = new TreeMap<>(hashMap);
        System.out.println("TreeMap 遍历顺序: " + treeMap.keySet()); // [1, 2, 3]
    }
}

6. 实战案例:学生成绩排名

下面通过一个完整的案例,演示如何使用 SortedMap 实现学生成绩的自动排名。

java 复制代码
import java.util.SortedMap;
import java.util.TreeMap;

public class ScoreRanking {
    public static void main(String[] args) {
        // 键为分数,值为学生姓名(分数相同则后者覆盖前者,实际可用 List 处理)
        SortedMap<Integer, String> ranking = new TreeMap<>();

        ranking.put(88, "张三");
        ranking.put(95, "李四");
        ranking.put(72, "王五");
        ranking.put(91, "赵六");

        System.out.println("=== 成绩从低到高排名 ===");
        for (var entry : ranking.entrySet()) {
            System.out.println(entry.getKey() + " 分 -> " + entry.getValue());
        }

        System.out.println("\n=== 前三名(成绩最高的 3 人)===");
        // 降序排列后取前 3
        SortedMap<Integer, String> desc = new TreeMap<>(java.util.Collections.reverseOrder());
        desc.putAll(ranking);

        int count = 0;
        for (var entry : desc.entrySet()) {
            if (count++ >= 3) break;
            System.out.println(entry.getKey() + " 分 -> " + entry.getValue());
        }
    }
}

7. 使用注意事项

7.1 null 键问题

TreeMap 不允许 null 键,因为无法对 null 进行排序比较。如果尝试插入 null 键,会抛出 NullPointerException。

java 复制代码
SortedMap<Integer, String> map = new TreeMap<>();
map.put(null, "value"); // 抛出 NullPointerException

7.2 键必须可比较

如果使用自然顺序,键必须实现 Comparable 接口;否则必须在构造时提供 Comparator,否则运行时抛出 ClassCastException。

7.3 子视图的边界

subMap 的 fromKey 必须小于 toKey,否则抛出 IllegalArgumentException。

8. 总结

SortedMap 是 Java 集合框架中处理有序键值对 的核心接口。通过 TreeMap 实现,我们可以轻松实现键的自动排序、范围查询和有序遍历。在实际开发中,当业务需要按顺序展示数据(如排行榜、时间线、字典序列表)时,SortedMap 是比 HashMap 更合适的选择。

掌握 SortedMap 的三大视图方法(subMap、headMap、tailMap)和自定义比较器,将帮助你在处理复杂排序需求时游刃有余。

相关推荐
死磕音视频13 分钟前
让小模型做选择题:限定候选项并读取 logits 概率的入门实践(附 Python 代码)
开发语言·python·大模型·文本分类·transformers
伯恩bourne2 小时前
Spring Boot Bean 注册的两种方式:@Component 家族 vs @Configuration + @Bean
java·spring boot·后端
silvamux_2 小时前
curl 测试 OpenAI 兼容 API 的完整流程
java·人工智能·缓存·ai·oneapi
省钱兄--zs2 小时前
深圳24小时自助健身房软硬件解决方案实战指南与开发流程详解
java·数据仓库·spring boot·小程序·架构
顽疲3 小时前
Java vue 养老系统源码详解:月账单 care_month_bill 与 Quartz 扣费 Job 实践
java·开发语言·vue.js·vue养老·springboot养老·java养老
Wang's Blog3 小时前
Java 项目实战: 交友项目 - 安装开发工具与环境准备
java·开发语言·交友
用户21296835261683 小时前
开源了一个 Java 多智能体 LLM 框架 Baton:一个依赖,把 Agent 循环、工具调用、记忆、编排全配齐
java·llm
helmsail3 小时前
05 管理活动与选品
java
天远数科3 小时前
Java数据工程:利用天远人车核验加强版优化智慧高速ETC发行合规体验
java·人工智能
.Bryce.3 小时前
在智能体中使用A2A工具:让大模型自己决定“该找谁帮忙“
android·java·服务器·redis·后端·agent·a2a