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)和自定义比较器,将帮助你在处理复杂排序需求时游刃有余。

相关推荐
小羊没烦恼!2 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
俊昭喜喜里2 天前
java中的继承和多态的区别
java
小羊没烦恼!2 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕2 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码2 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖2 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时2 天前
01-06-A-JVM排查实战详解
java·jvm
伞伞悦读2 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
vipxieliang2 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot
C语言小火车2 天前
C/C++ 为什么需要编译器?
开发语言·c++