代码编织梦想

在日常开发中,处理各种数据范围和区间是一个常见的需求。Google的Guava库为我们提供了一个强大的工具——RangeMap,用于处理这种基于范围的映射问题。本文将深入探讨RangeMap的设计原理、使用方法和实际应用场景。

提升编程效率的利器: 解析Google Guava库之集合篇Immutable(一)
提升编程效率的利器: 解析Google Guava库之集合篇Multimap(二)
提升编程效率的利器: 解析Google Guava库之集合篇BitMap(三)
提升编程效率的利器: 解析Google Guava库之集合篇Table二维映射(四)
提升编程效率的利器: 解析Google Guava库之集合篇RangeSet范围集合(五)

一、RangeMap概述

RangeMap是Guava提供的一种特殊的映射结构,它将不相交、且不为空的Range(范围)映射到一个特定的值。与传统的Map不同,RangeMap的键是一个范围而不是单个元素。这种映射关系使得RangeMap在处理需要根据不同的范围来确定不同的行为或结果的问题时非常有用。

可以理解为RangeMap是RangeSet的进阶版本(关于RangeSet请参考:提升编程效率的利器: 解析Google Guava库之集合篇RangeSet范围集合(五)),它建立了区间与特定值之间的映射关系。与RangeSet不同的是,RangeSet会自动合并相邻的区间并仅维护一个区间范围,而RangeMap则明确了区间范围与对应值之间的联系。当在已有映射的区间中插入相交的新区间时,相交部分的值会被新值覆盖,同时原区间会被拆分。此外,RangeMap不提供补集操作的功能。

二、RangeMap的核心特性

  • 不合并相邻的映射:RangeMap从不自动合并相邻的范围,即使这些相邻的范围映射到相同的值。这意味着每个范围都是独立且不相交的。
  • 基于红黑树的实现:Guava中的TreeRangeMap是基于红黑树实现的,红黑树是一种自平衡的二叉查找树。这种数据结构保证了RangeMap能够提供高效的范围查找和插入操作。
  • 灵活的范围定义:RangeMap支持各种范围定义,包括开区间、闭区间、半开半闭区间等。这为我们提供了极大的灵活性,可以根据实际需求定义范围。
  • TreeRangeMap 插入重叠区间的行为:
    当你尝试向 TreeRangeMap 插入一个与已保存区间发生重叠的新区间时,TreeRangeMap 会采取以下行为:
    • 切割原有区间:为了确保每个区间都是互不重叠的,TreeRangeMap 会对与新区间重叠的已保存区间进行切割。这意味着原有的区间会被分割成多个小区间,以便为新区间腾出空间。
    • 保留插入区间的完整性:切割操作会确保您正在插入的新区间保持完整,不会被分割成多个部分。这是为了维护您的意图,即您希望这个特定的、连续的区间映射到某个特定的值。
  • 通过单个值 K 来查询:
    虽然 TreeRangeMap 是以区间作为键来存储数据的,但你可以通过单个值 K 来查询它。当您调用 get(K key) 方法时:
    • 查找对应的区间:TreeRangeMap 会使用其内部的红黑树结构来高效地查找包含给定值 K 的区间。由于区间是排序的,并且树结构允许快速查找,因此这个过程通常是相当高效的。
    • 返回区间对应的值:一旦找到了包含值 K 的区间,TreeRangeMap 就会返回该区间映射的值。

三、如何使用RangeMap

使用RangeMap的基本步骤如下:

  • 引入Guava库:首先,确保你的项目中已经引入了Guava库。
  • 创建RangeMap:使用ImmutableRangeMap.builder()或TreeRangeMap.create()方法创建一个RangeMap实例。
  • 添加映射关系:使用put方法将范围映射到特定的值。注意,添加的范围必须是不相交的。
  • 查询和获取值:使用get方法根据给定的范围或值获取映射的结果。

常用方法:
在这里插入图片描述
使用示例:

import com.google.common.collect.Range;  
import com.google.common.collect.RangeMap;  
import com.google.common.collect.TreeRangeMap;  
  
import java.util.Map;  
  
public class TreeRangeMapExample {  
    public static void main(String[] args) {  
        // 创建一个空的 TreeRangeMap  
        RangeMap<Integer, String> rangeMap = TreeRangeMap.create();  
  
        // 向 RangeMap 添加映射关系  
        rangeMap.put(Range.closed(0, 4), "Low");  
        rangeMap.put(Range.open(5, 10), "Medium");  
        rangeMap.put(Range.closedOpen(10, 15), "High");  
        rangeMap.put(Range.greaterThan(15), "Very High");  
  
        // 使用 get 方法获取单个值对应的映射  
        System.out.println(rangeMap.get(2));     // 输出: Low  
        System.out.println(rangeMap.get(5));     // 输出: null (因为5不包含在任何区间内)  
        System.out.println(rangeMap.get(7));     // 输出: Medium  
        System.out.println(rangeMap.get(10));    // 输出: null (因为10只包含在半开半闭区间内,这里应使用get(Range<K> range)方法)  
        System.out.println(rangeMap.get(12));    // 输出: High  
        System.out.println(rangeMap.get(20));    // 输出: Very High  
  
        // 使用 get(Range<K> range) 方法获取区间对应的映射  
        System.out.println(rangeMap.get(Range.singleton(10)));  // 输出: High  
  
        // 使用 subRangeMap 方法获取子 RangeMap  
        RangeMap<Integer, String> subRangeMap = rangeMap.subRangeMap(Range.closedOpen(6, 12));  
        System.out.println(subRangeMap.asMapOfRanges());  // 输出: {(6, 10) => Medium, [10, 12) => High}  
  
        // 使用 asMapOfRanges 方法获取整个 RangeMap 的视图  
        System.out.println(rangeMap.asMapOfRanges());  // 输出整个映射关系  
  
        // 使用 remove 方法移除映射关系  
        rangeMap.remove(Range.closed(0, 4));  
        System.out.println(rangeMap.asMapOfRanges());  // 输出移除 [0, 4] 区间后的映射关系  
  
        // 遍历 RangeMap  
        for (Map.Entry<Range<Integer>, String> entry : rangeMap.asMapOfRanges().entrySet()) {  
            System.out.println(entry.getKey() + " => " + entry.getValue());  
        }  
    }  
}

请注意,get(K key)方法的行为可能会根据键K落在哪个区间而返回相应的值,或者如果没有区间包含该键则返回null。对于刚好位于区间边界的值,要根据区间的开闭性质来判断是否包含在内。例如,在上述示例中,rangeMap.get(10)返回null,因为10并不包含在任何定义的区间内([10, 15)是左闭右开的,不包括15,且(5, 10)是左开右开的,不包括10)。

此外,asMapOfRanges()方法返回一个视图,它展示了RangeMap中的所有映射关系。这个视图对于调试和理解RangeMap的内容非常有用。

再看下重叠区间的行为

   // 创建一个空的 TreeRangeMap  
        RangeMap<Integer, String> rangeMap = TreeRangeMap.create();  
  
        // 向 RangeMap 添加映射关系  
        rangeMap.put(Range.closed(0, 4), "Low");  
        rangeMap.put(Range.open(5, 10), "Medium");  
        rangeMap.put(Range.closedOpen(10, 15), "High");  
        rangeMap.put(Range.greaterThan(15), "Very High");  
  
        // 演示插入重叠区间的场景  
        // 插入一个与现有区间重叠的新区间 [3, 8]  
        rangeMap.put(Range.closedOpen(3, 8), "Overlap");  
  
        // 打印插入重叠区间后的映射关系  
        System.out.println(rangeMap.asMapOfRanges());  
        // 输出:  {[0, 3]=Low, (3, 8)=Overlap, [8, 10)=Medium, [10, 15)=High, (15, ∞)=Very High}  
  
        // 演示使用 span() 方法,返回包含给定范围内所有键值的映射关系  
        Range<Integer> queryRange = Range.closedOpen(2, 12);  
        RangeMap<Integer, String> spannedRangeMap = rangeMap.span(queryRange);  
  
        // 打印 span() 方法的结果  
        System.out.println(spannedRangeMap.asMapOfRanges());  
        // 输出: {(2, 3]=Low, [3, 8)=Overlap, [8, 10)=Medium, [10, 12)=High}  
  
        // 使用 lowerEndpoint() 和 upperEndpoint() 方法获取 span() 结果的边界  
        Integer lowerEndpoint = spannedRangeMap.span().lowerEndpoint();  
        Integer upperEndpoint = spannedRangeMap.span().upperEndpoint();  
  
        // 打印边界值  
        System.out.println("Lower endpoint of the spanned range: " + lowerEndpoint); // 输出 2  
        System.out.println("Upper endpoint of the spanned range: " + upperEndpoint); // 输出 12  
  
        // 注意:上面的 lowerEndpoint 和 upperEndpoint 调用方式实际上是不正确的,  
        // 因为 span() 方法在这里没有参数,它会返回整个 RangeMap,而不是查询的范围。  
        // 正确的做法是使用 queryRange 的 lowerEndpoint 和 upperEndpoint 方法,如下:  
  
        System.out.println("Correct lower endpoint of the query range: " + queryRange.lowerEndpoint()); // 应该输出 2  
        System.out.println("Correct upper endpoint of the query range: " + queryRange.upperEndpoint()); // 应该输出 12  
    }  

四、RangeMap的应用场景

  • 重试机制: 在分布式系统中,可以使用RangeMap实现基于时间的重试机制。将时间范围映射到不同的重试策略上,可以灵活地控制重试行为。
  • 阶梯式收费: 在计费系统中,RangeMap可以方便地实现阶梯式收费模型。将不同的使用量或消费金额范围映射到不同的费用上,可以简化计费逻辑。
  • 配置管理: 在复杂的系统中,可能需要根据不同的配置项来确定不同的行为。使用RangeMap管理这些配置项,可以将配置项的范围映射到对应的行为上,提高配置管理的灵活性。

五、总结

Guava库中的RangeMap为我们提供了一种方便、灵活的方式来处理基于范围的映射问题。通过合理地使用RangeMap,我们可以简化代码逻辑,提高代码的可读性和可维护性。在实际开发中,我们应该根据具体需求选择合适的范围映射工具,以提高开发效率和代码质量。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_26664043/article/details/135948610

cms垃圾回收器为什么被移除-爱代码爱编程

CMS(Concurrent Mark-Sweep)垃圾回收器是Java虚拟机中一种以获取最短回收停顿时间为目标的收集器。CMS的主要特点是它允许垃圾回收线程与应用程序线程同时运行,尽可能减少应用程序的停顿时间。尽管它在多核

jdk8新特性-爱代码爱编程

一、介绍 1、简介 Java的Lambda表达式是Java 8引入的一个特性,它支持函数式编程,允许将函数作为方法的参数或返回值,从而简化了匿名内部类的使用,并提供了对并行编程的更好支持。 2、语法 Lambda表达式的使用前提是存在一个接口,该接口中有且只有一个抽象方法。在主方法中可以通过创建接口的匿名内部类或使用Lambda表达式来调用该接

jar包增量更新分析-爱代码爱编程

jdk自带工具jdeps,可分析class依赖关系(依赖的其它类和jar)。 团队,可以在此工具结果的基础上再详细分析对比出增量文件; 思路如下: jdeps分别分析出旧包和新包的文件依赖关系。并对比出新增的文件列表、删

java实现拼图小游戏-爱代码爱编程

效果演示 java-拼图游戏效果演示   分为三个内容 1.游戏的功能主要实现以及玩法 2.游戏登录界面的设计 3.随机验证码的生成 游戏中的素材的话,我放到网盘了,需要的自取 不过我只调整了一张图片,其他的都是随便切上去的效果差,可以自己找大小相同的图片来当素材比较好。 链接:http://链接:https://pa

android model引入其他aar包 导致无法打包成aar-爱代码爱编程

场景:在Model(mylibrary) 中引入了一个aar包,然后又想将mylibrary生成一个aar给其他模块使用。生成aar包时报错,不让生成。 报错:构建aar时不支持直接的本地.aar文件依赖关系。生成的AAR将被破坏,因为来自任何本地.AAR文件依赖项的类和Android资源都不会打包在生成的AAR中。 Direct local .aar

java中实现缓存的几种方式-爱代码爱编程

一、通过HashMap实现缓存 这种方式可以简单实现本地缓存,但是实际开发中不推荐使用,下面我们来实现一下这种方式。 首先创建一个管理缓存的类 public class LocalCache { public static ConcurrentMap<String,String> cache = new ConcurrentHas

详解java google guava_java guava 许可-爱代码爱编程

详细介绍         Google Guava是Google为Java开发的开源库集合,它提供了丰富的工具类和集合框架的扩展,旨在提高开发效率和代码质量。Guava包括但不限于集合操作、并发编程辅助、缓存机制、字符串处理、I/O操作、原生类型支持、常见算法实现、函数式编程支持、测试库等模块。其核心设计原则是简化编程模型、减少代码量、提高代码的可读性和

深入解析google guava库与spring retry重试框架_java开源重试框架对比及示例-爱代码爱编程

码到三十五 : 个人主页 心中有诗画,指尖舞代码,目光览世界,步履越千山,人间尽值得 ! 在分布式系统和网络应用中,重试机制是一种常见且重要的容错手段。当操作因各种原

提升编程效率的利器: 解析google guava库之集合篇bitmap(三)_bimap-爱代码爱编程

Google Guava库提供了大量高效、有用的Java工具类,其中BiMap是一种非常独特的映射结构。与传统的Map不同,BiMap保证了键和值都是唯一的,因此它提供了键到值以及值到键的双向映射。这种数据结构在处理需

提升编程效率的利器: 解析google guava库之集合篇table二维映射(四)_谷歌表格的映射-爱代码爱编程

在Java开发中,我们经常使用Map数据结构来存储键值对,其中键是唯一的,可以快速查找到对应的值。但在某些场景下,我们可能需要一个更复杂的映射结构,其中键由两部分组成,类似于一个二维表格的行和列。Guava库中的Tab

提升编程效率的利器: 解析google guava库之集合篇rangeset范围集合(五)_treerangeset原理-爱代码爱编程

在编程中,我们经常需要处理各种范围集合,例如时间范围、数字范围等。传统的集合类库往往只能处理离散的元素集合,对于范围集合的处理则显得力不从心。为了解决这个问题,Google的Guava库提供了一种强大的数据结构——Range

提升编程效率的利器: 解析google guava库之集合篇immutable(一)_google immutablelist-爱代码爱编程

Guava库提供了丰富的集合类API,这些API扩展了Java标准库中的集合功能,提供了更多的灵活性和便利性。 在日常开发中,集合类是我们日常编程不可或缺的一部分。Java标准库为我们提供了一套基本的集合类,但在

设计模式-爱代码爱编程

作者持续关注 WPS二次开发专题系列,持续为大家带来更多有价值的WPS开发技术细节,如果能够帮助到您,请帮忙来个一键三连,更多问题请联系我(QQ:250325397) 目录 定义 特点 使用场景 优缺点 (1) 优点 (2) 缺点 模式结构 具体实现 实际应用 定义 责任链(Chain of Respons