首页 > 编程语言 >Java TreeMap 介绍与使用

Java TreeMap 介绍与使用

时间:2023-07-12 20:33:06浏览次数:42  
标签:map Java TreeMap 介绍 System 键值 println out

介绍

TreeMap 是 Java 集合框架中的一个类,它实现了 SortedMap 接口,可以存储键值对,并按照键的自然顺序或者指定的比较器进行排序。TreeMap 的底层是一棵红黑树,这是一种自平衡的二叉搜索树,可以保证在插入,删除,查找等操作中的时间复杂度为 O(log n)。

使用

要使用 TreeMap,我们需要导入 java.util 包,并创建一个 TreeMap 对象。我们可以指定键和值的类型,也可以使用泛型。例如:

import java.util.*;

// 创建一个 TreeMap,键为 String 类型,值为 Integer 类型
TreeMap<String, Integer> map = new TreeMap<>();

// 创建一个 TreeMap,使用泛型
TreeMap<String, Integer> map = new TreeMap<>();

我们可以使用 put 方法向 TreeMap 中添加键值对,如果键已经存在,则会覆盖原来的值。我们可以使用 get 方法根据键获取对应的值,如果键不存在,则会返回 null。我们可以使用 remove 方法根据键删除对应的键值对,如果键不存在,则不会有任何影响。我们可以使用 size 方法获取 TreeMap 中的键值对的数量。我们可以使用 containsKey 方法判断 TreeMap 中是否包含某个键。我们可以使用 containsValue 方法判断 TreeMap 中是否包含某个值。我们可以使用 clear 方法清空 TreeMap 中的所有键值对。例如:

import java.util.*;

public class TreeMapDemo {
    public static void main(String[] args) {
        // 创建一个 TreeMap
        TreeMap<String, Integer> map = new TreeMap<>();

        // 向 TreeMap 中添加键值对
        map.put("Alice", 90);
        map.put("Bob", 80);
        map.put("Charlie", 70);
        map.put("David", 60);

        // 打印 TreeMap 的内容
        System.out.println(map); // {Alice=90, Bob=80, Charlie=70, David=60}

        // 根据键获取对应的值
        System.out.println(map.get("Alice")); // 90
        System.out.println(map.get("Eve")); // null

        // 根据键删除对应的键值对
        map.remove("Bob");
        System.out.println(map); // {Alice=90, Charlie=70, David=60}

        // 获取 TreeMap 中的键值对的数量
        System.out.println(map.size()); // 3

        // 判断 TreeMap 中是否包含某个键
        System.out.println(map.containsKey("Charlie")); // true
        System.out.println(map.containsKey("Eve")); // false

        // 判断 TreeMap 中是否包含某个值
        System.out.println(map.containsValue(70)); // true
        System.out.println(map.containsValue(50)); // false

        // 清空 TreeMap 中的所有键值对
        map.clear();
        System.out.println(map); // {}
    }
}

除了上述方法外,TreeMap 还提供了一些特有的方法,用于利用其排序特性进行操作。例如:

  • firstKey 和 lastKey:分别返回 TreeMap 中最小和最大的键。
  • lowerKey 和 higherKey:分别返回 TreeMap 中小于和大于给定键的最近的键。
  • floorKey 和 ceilingKey:分别返回 TreeMap 中小于等于和大于等于给定键的最近的键。
  • subMap:返回一个子映射,包含给定范围内的所有键值对。
  • headMap 和 tailMap:分别返回一个子映射,包含小于或等于给定键或大于或等于给定键的所有键值对。
  • firstEntry 和 lastEntry:分别返回 TreeMap 中最小和最大的键值对。
  • lowerEntry 和 higherEntry:分别返回 TreeMap 中小于和大于给定键的最近的键值对。
  • floorEntry 和 ceilingEntry:分别返回 TreeMap 中小于等于和大于等于给定键的最近的键值对。
  • pollFirstEntry 和 pollLastEntry:分别返回并删除 TreeMap 中最小和最大的键值对。

例如:

import java.util.*;

public class TreeMapDemo2 {
    public static void main(String[] args) {
        // 创建一个 TreeMap
        TreeMap<String, Integer> map = new TreeMap<>();

        // 向 TreeMap 中添加键值对
        map.put("Alice", 90);
        map.put("Bob", 80);
        map.put("Charlie", 70);
        map.put("David", 60);

        // 打印 TreeMap 的内容
        System.out.println(map); // {Alice=90, Bob=80, Charlie=70, David=60}

        // 返回 TreeMap 中最小和最大的键
        System.out.println(map.firstKey()); // Alice
        System.out.println(map.lastKey()); // David

        // 返回 TreeMap 中小于和大于给定键的最近的键
        System.out.println(map.lowerKey("Bob")); // Alice
        System.out.println(map.higherKey("Bob")); // Charlie

        // 返回 TreeMap 中小于等于和大于等于给定键的最近的键
        System.out.println(map.floorKey("Bob")); // Bob
        System.out.println(map.ceilingKey("Bob")); // Bob

        // 返回一个子映射,包含给定范围内的所有键值对
        System.out.println(map.subMap("Alice", true, "Charlie", true)); // {Alice=90, Bob=80, Charlie=70}

        // 返回一个子映射,包含小于或等于给定键或大于或等于给定键的所有键值对
        System.out.println(map.headMap("Charlie", true)); // {Alice=90, Bob=80, Charlie=70}
        System.out.println(map.tailMap("Charlie", true)); // {Charlie=70, David=60}

        // 返回 TreeMap 中最小和最大的键值对
        System.out.println(map.firstEntry()); // Alice=90
        System.out.println(map.lastEntry()); // David=60

        // 返回 TreeMap 中小于和大于给定键的最近的键值对
        System.out.println(map.lowerEntry("Bob")); // Alice=90
        System.out.println(map.higherEntry("Bob")); // Charlie=70

        // 返回 TreeMap 中小于等于和大于等于给定键的最近的键值对
        System.out.println(map.floorEntry("Bob")); // Bob=80
        System.out.println(map.ceilingEntry("Bob")); // Bob=80

        // 返回并删除 TreeMap 中最小和最大的键值对
        System.out.println(map.pollFirstEntry()); // Alice=90
        System.out.println(map.pollLastEntry()); // David=60

    }
}

适用场景

TreeMap 是一种有序的映射,它可以在保证高效性的同时,提供一些基于排序的操作。因此,TreeMap 适用于以下场景:

  • 需要按照键的自然顺序或者指定的比较器进行排序的场景,例如字典,排行榜,日程安排等。
  • 需要快速找到映射中最小或者最大的键或者值的场景,例如优先队列,堆,区间查询等。
  • 需要快速找到映射中某个范围内的所有键或者值的场景,例如范围搜索,前缀匹配,区间统计等。

底层原理

TreeMap 的底层是一棵红黑树,这是一种自平衡的二叉搜索树,它满足以下性质:

  • 每个节点要么是红色,要么是黑色。
  • 根节点是黑色。
  • 每个叶子节点(空节点)是黑色。
  • 如果一个节点是红色,那么它的两个子节点都是黑色。
  • 从任意一个节点到其每个叶子节点的所有路径都包含相同数目的黑色节点。

这些性质保证了红黑树在插入,删除,查找等操作中都能保持平衡

源码分析

我们来看一下 TreeMap 的源码,主要关注它的内部类,构造方法,和一些重要的方法。

内部类

TreeMap 的内部类有以下几个:

  • Entry:表示树中的一个节点,包含键,值,颜色,左子节点,右子节点,父节点等属性。
  • KeySet:表示键的集合,实现了 NavigableSet 接口,提供了一些基于排序的操作。
  • Values:表示值的集合,实现了 Collection 接口,提供了一些基本的操作。
  • EntrySet:表示键值对的集合,实现了 Set 接口,提供了一些基本的操作。
  • PrivateEntryIterator:表示树中节点的迭代器,实现了 Iterator 接口,提供了 next 和 remove 方法。
  • KeyIterator:继承自 PrivateEntryIterator,用于遍历键。
  • ValueIterator:继承自 PrivateEntryIterator,用于遍历值。
  • EntryIterator:继承自 PrivateEntryIterator,用于遍历键值对。
  • AscendingSubMap:表示一个升序的子映射,实现了 NavigableMap 接口,提供了一些基于排序的操作。
  • DescendingSubMap:表示一个降序的子映射,实现了 NavigableMap 接口,提供了一些基于排序的操作。
  • AscendingKeySetIterator:用于遍历升序子映射中的键。
  • DescendingKeySetIterator:用于遍历降序子映射中的键。

构造方法

TreeMap 的构造方法有以下几个:

  • TreeMap():创建一个空的 TreeMap,按照键的自然顺序进行排序。
  • TreeMap(Comparator<? super K> comparator):创建一个空的 TreeMap,并指定一个比较器来对键进行排序。
  • TreeMap(Map<? extends K,? extends V> m):创建一个 TreeMap,并将给定映射中的所有键值对添加到 TreeMap 中,并按照键的自然顺序进行排序。
  • TreeMap(SortedMap<K,? extends V> m):创建一个 TreeMap,并将给定有序映射中的所有键值对添加到 TreeMap 中,并按照给定有序映射中的比较器或者自然顺序进行排序。

重要方法

TreeMap 的重要方法有以下几个:

  • put(K key, V value):向 TreeMap 中添加一个键值对,如果键已经存在,则覆盖原来的值,并返回原来的值。如果键不存在,则返回 null。该方法会调用 putVal 方法来实现插入操作。putVal 方法会先判断树是否为空,如果为空,则直接创建一个黑色的根节点。如果不为空,则从根节点开始遍历树,根据比较器或者自然顺序来比较给定键和当前节点的键。如果相等,则覆盖当前节点的值。如果小于,则向左子树遍历。如果大于,则向右子树遍历。如果遇到空节点,则在该位置创建一个红色的新节点,并将其连接到父节点上。然后调用 fixAfterInsertion 方法来修复插入后可能导致红黑树性质被破坏的情况。fixAfterInsertion 方法会根据不同的情况进行不同的旋转和变色操作,以保证红黑树性质得到恢复。

  • get(Object key):根据给定键获取对应的值,如果键不存在,则返回 null。该方法会调用 getEntry 方法来实现查找操作。getEntry 方法会从根节点开始遍历树,根据比较器或者自然顺序来比较给定键和当前节点的键。如果相等,则返回当前节点。如果小于,则向左子树遍历。如果大于,则向右子树遍历。如果遇到空节点,则返回 null。

  • remove(Object key):根据给定键删除对应的键值对,如果键不存在,则不会有任何影响,并返回 null。如果键存在,则返回被删除的值。该方法会调用 getEntry 方法来找到要删除的节点,然后调用 deleteEntry 方法来实现删除操作。deleteEntry 方法会先判断要删除的节点是否有两个非空的子节点,如果有,则找到它的后继节点(右子树中最小的节点),并将后继节点的键值复制到要删除的节点上,然后将后继节点作为要删除的节点。然后判断要删除的节点是否有一个非空的子节点,如果有,则将该子节点替换要删除的节点,并将其连接到父节点上。如果没有,则直接断开要删除的节点和父节点的连接。然后判断要删除的节点是否是黑色,如果是,则调用 fixAfterDeletion 方法来修复删除后可能导致红黑树性质被破坏的情况。fixAfterDeletion 方法会根据不同的情况进行不同的旋转和变色操作,以保证红黑树性质得到恢复。

  • firstKey():返回 TreeMap 中最小的键,如果 TreeMap 为空,则抛出 NoSuchElementException 异常。该方法会调用 getFirstEntry 方法来实现查找操作。getFirstEntry 方法会从根节点开始,沿着左子树一直向下遍历,直到遇到空节点,然后返回其父节点。

  • lastKey():返回 TreeMap 中最大的键,如果 TreeMap 为空,则抛出 NoSuchElementException 异常。该方法会调用 getLastEntry 方法来实现查找操作。getLastEntry 方法会从根节点开始,沿着右子树一直向下遍历,直到遇到空节点,然后返回其父节点。

  • lowerKey(K key):返回 TreeMap 中小于给定键的最近的键,如果没有这样的键,则返回 null。该方法会调用 getLowerEntry 方法来实现查找操作。getLowerEntry 方法会从根节点开始遍历树,根据比较器或者自然顺序来比较给定键和当前节点的键。如果小于等于,则向左子树遍历,并记录当前节点为候选结果。如果大于,则向右子树遍历,并不更新候选结果。如果遇到空节点,则返回候选结果。

  • higherKey(K key):返回 TreeMap 中大于给定键的最近的键,如果没有这样的键,则返回 null。该方法会调用 getHigherEntry 方法来实现查找操作。getHigherEntry 方法会从根节点开始遍历树,根据比较器或者自然顺序来比较给定键和当前节点的键。如果小于,则向左子树遍历,并不更新候选结果。如果大于等于,则向右子树遍历,并记录当前节点为候选结果。如果遇到空节点,则返回候选结果。

  • floorKey(K key):返回 TreeMap 中小于等于给定键的最近的键,如果没有这样的键,则返回 null。该方法会调用 getFloorEntry 方法来实现查找操作。getFloorEntry 方法会从根节点开始遍历树,根据比较器或者自然顺序来比较给定键和当前节点的键。如果小于,则向左子树遍历,并不更新候选结果。如果等于,则直接返回当前节点。如果大于,则向右子树遍历,并记录当前节点为候选结果。如果遇到空节点,则返回候选结果。

  • ceilingKey(K key):返回 TreeMap 中大于等于给定键的最近的键,如果没有这样的键,则返回 null。该方法会调用 getCeilingEntry 方法来实现查找操作。getCeilingEntry 方法会从根节点开始遍历树

  • 根据比较器或者自然顺序来比较给定键和当前节点的键。如果小于,则向左子树遍历,并记录当前节点为候选结果。如果等于,则直接返回当前节点。如果大于,则向右子树遍历,并不更新候选结果。如果遇到空节点,则返回候选结果。

    • subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive):返回一个子映射,包含给定范围内的所有键值对,根据 fromInclusive 和 toInclusive 参数来决定是否包含边界值。如果 fromKey 大于 toKey,则抛出 IllegalArgumentException 异常。该方法会创建一个 AscendingSubMap 对象,并将给定的参数传递给它的构造方法。

    • headMap(K toKey, boolean inclusive):返回一个子映射,包含小于或等于给定键的所有键值对,根据 inclusive 参数来决定是否包含边界值。该方法会创建一个 AscendingSubMap 对象,并将 null,false,toKey,inclusive 作为参数传递给它的构造方法。

    • tailMap(K fromKey, boolean inclusive):返回一个子映射,包含大于或等于给定键的所有键值对,根据 inclusive 参数来决定是否包含边界值。该方法会创建一个 AscendingSubMap 对象,并将 fromKey,inclusive,null,false 作为参数传递给它的构造方法。

    • firstEntry():返回 TreeMap 中最小的键值对,如果 TreeMap 为空,则返回 null。该方法会调用 getFirstEntry 方法来实现查找操作。

    • lastEntry():返回 TreeMap 中最大的键值对,如果 TreeMap 为空,则返回 null。该方法会调用 getLastEntry 方法来实现查找操作。

    • lowerEntry(K key):返回 TreeMap 中小于给定键的最近的键值对,如果没有这样的键值对,则返回 null。该方法会调用 getLowerEntry 方法来实现查找操作。

    • higherEntry(K key):返回 TreeMap 中大于给定键的最近的键值对,如果没有这样的键值对,则返回 null。该方法会调用 getHigherEntry 方法来实现查找操作。

    • floorEntry(K key):返回 TreeMap 中小于等于给定键的最近的键值对,如果没有这样的键值对,则返回 null。该方法会调用 getFloorEntry 方法来实现查找操作。

    • ceilingEntry(K key):返回 TreeMap 中大于等于给定键的最近的键值对,如果没有这样的键值对,则返回 null。该方法会调用 getCeilingEntry 方法来实现查找操作。

    • pollFirstEntry():返回并删除 TreeMap 中最小的键值对,如果 TreeMap 为空,则返回 null。该方法会调用 getFirstEntry 方法来找到要删除的节点,然后调用 deleteEntry 方法来实现删除操作。

    • pollLastEntry():返回并删除 TreeMap 中最大的键值对,如果 TreeMap 为空,则返回 null。该方法会调用 getLastEntry 方法来找到要删除的节点,然后调用 deleteEntry 方法来实现删除操作。

    注意事项

    使用 TreeMap 时,需要注意以下几点:

    • 如果使用自然顺序进行排序,则需要保证键是可比较的(实现了 Comparable 接口),否则会抛出 ClassCastException 异常。
    • 如果使用指定的比较器进行排序,则需要保证比较器是一致的(满足自反性,对称性,传递性),否则可能导致排序结果不正确或者抛出 ClassCastException 异常。
    • 如果在遍历 TreeMap 的过程中修改了其结构(添加或删除了元素),则可能导致 ConcurrentModificationException 异常或者不确定的行为。
    • 如果在遍历 TreeMap 的过程中修改了其元素(改变了键或者值),则可能导致排序结果不正确或者不确定的行为。
    • TreeMap 不是线程安全的,如果多个线程同时访问或修改 TreeMap,则需要进行同步处理。

    总结

    TreeMap 是一种有序的映射,它可以存储键值对,并按照键的自然顺序或者指定的比较器进行排序。TreeMap 的底层是一棵红黑树,这是一种自平衡的二叉搜索树,可以保证在插入,删除,查找等操作中的时间复杂度为 O(log n)。TreeMap 还提供了一些特有的方法,用于利用其排序特性进行操作。使用 TreeMap 时,需要注意一些细节,以避免出现异常或者不确定的行为。TreeMap 是 Java 集合框架中的一个重要的类,它在很多场景中都有着广泛的应用。

标签:map,Java,TreeMap,介绍,System,键值,println,out
From: https://www.cnblogs.com/shoshana-kong/p/17548762.html

相关文章

  • 你知道Java是世界第一的秘密吗?
    说Java你会说他就是一个计算机语言吧,对它并不是很了解。看完下面的文字,你肯定就不会说你对Java不了解了。Java从1995年诞生到现在已经21年了,他的辉煌你知道吗?Java一直在改变你的生活!傲居语言排行榜榜首Java在TIOBE上的位置TIOBE编程语言社区排行榜是编程语言流行趋势的一个指标,每......
  • JAVA 数字类型 的使用和选择
    JAVA语言中有八种基本的数字类型,分别是byte、short、int、long、float、double、char和boolean。这些类型的区别在于它们所占用的内存空间和表示的范围不同。在使用和选择数字类型时,需要考虑以下几个因素:数字的大小:如果数字很小,可以使用byte或short类型,它们占用1个字......
  • JMeter脚本报错:Cannot find engine named: 'javascript'的解决方法
    本文将介绍如何解决在JMeter版本5.4.1下执行脚本时出现的错误信息“javax.script.ScriptException:Cannotfindenginenamed:'javascript'”。通过将本地JDK版本从18.0.1.1更改为1.8.0_151来解决此问题。当使用JMeter进行脚本执行时,有时可能会遇到以下错误信息:javax.script......
  • Java 基础 - 异常随笔
     异常基础总结try、catch和finally都不能单独使用,只能是try-catch、try-finally或者try-catch-finally。try语句块监控代码,出现异常就停止执行下面的代码,然后将异常移交给catch语句块来处理。catch–用于捕获异常。catch用来捕获try语句块中发生的异常。finally语句块中的......
  • Java字符串逆序的四种方法及比较
    Java中实现字符串逆序有以下几种常见的方法:方法一:使用StringBuffer或StringBuilder的reverse()方法。这是最简单和最直接的方法,只需要将String对象转换为StringBuffer或StringBuilder对象,然后调用它们的reverse()方法,就可以得到逆序的字符串。例如:publicclassStringReverse......
  • 不确定大小的数组怎么办?Java中三种常用的方法
    Java中如何操作不确定大小的数组1. 前言 1.1 什么是数组数组是一种存储多个相同类型数据的有序集合,它可以通过索引来访问每个元素。数组是一种引用类型的变量,它在内存中占用一块连续的空间。 1.2  数组的特点数组有以下几个特点:-数组的长度是确定的,一旦创建就不能......
  • Java实现浏览器端大文件分片上传解决方案
    ​ 上周遇到这样一个问题,客户上传高清视频(1G以上)的时候上传失败。一开始以为是session过期或者文件大小受系统限制,导致的错误。查看了系统的配置文件没有看到文件大小限制,web.xml中seesiontimeout是30,我把它改成了120。但还是不行,有时候10分钟就崩了。同事说,可能是客户这里......
  • Java Map 通过key过滤
    pom文件:<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.1-jre</version></dependency>代码:packagecom.example.core.utils.collections;importcom.google.common.......
  • 你信不信,只要学几天javascript就可以使用纯原生实现五星评分效果 【附完整代码】
    ......
  • Java复制(拷贝)数组的4种方法:arraycopy()方法、clone() 方法、copyOf()和copyOfRange
    http://c.biancheng.net/view/924.html所谓复制数组,是指将一个数组中的元素在另一个数组中进行复制。本文主要介绍关于 Java 里面的数组复制(拷贝)的几种方式和用法。在Java中实现数组复制分别有以下4种方法:Arrays类的copyOf()方法Arrays类的copyOfRange()方法Syst......