提升编程效率的利器: 解析Google Guava库之集合篇RangeMap范围映射(六)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 提升编程效率的利器: 解析Google Guava库之集合篇RangeMap范围映射(六)

在日常开发中,处理各种数据范围和区间是一个常见的需求。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,我们可以简化代码逻辑,提高代码的可读性和可维护性。在实际开发中,我们应该根据具体需求选择合适的范围映射工具,以提高开发效率和代码质量。

相关文章
|
1月前
|
SQL Oracle 关系型数据库
SQL整库导出语录:全面解析与高效执行策略
在数据库管理和维护过程中,整库导出是一项常见的需求,无论是为了备份、迁移还是数据分析,掌握如何高效、准确地导出整个数据库至关重要
|
2月前
|
XML JSON 网络协议
超级好用的C++实用库之字节流解析器
超级好用的C++实用库之字节流解析器
28 3
|
2月前
|
数据采集 存储 JSON
从零到一构建网络爬虫帝国:HTTP协议+Python requests库深度解析
在网络数据的海洋中,网络爬虫遵循HTTP协议,穿梭于互联网各处,收集宝贵信息。本文将从零开始,使用Python的requests库,深入解析HTTP协议,助你构建自己的网络爬虫帝国。首先介绍HTTP协议基础,包括请求与响应结构;然后详细介绍requests库的安装与使用,演示如何发送GET和POST请求并处理响应;最后概述爬虫构建流程及挑战,帮助你逐步掌握核心技术,畅游数据海洋。
67 3
|
2月前
|
缓存 网络协议 分布式数据库
超级好用的C++实用库之DNS解析
超级好用的C++实用库之DNS解析
50 0
|
2月前
|
设计模式 存储 算法
PHP中的设计模式:策略模式的深入解析与应用在软件开发的浩瀚海洋中,PHP以其独特的魅力和强大的功能吸引了无数开发者。作为一门历史悠久且广泛应用的编程语言,PHP不仅拥有丰富的内置函数和扩展库,还支持面向对象编程(OOP),为开发者提供了灵活而强大的工具集。在PHP的众多特性中,设计模式的应用尤为引人注目,它们如同精雕细琢的宝石,镶嵌在代码的肌理之中,让程序更加优雅、高效且易于维护。今天,我们就来深入探讨PHP中使用频率颇高的一种设计模式——策略模式。
本文旨在深入探讨PHP中的策略模式,从定义到实现,再到应用场景,全面剖析其在PHP编程中的应用价值。策略模式作为一种行为型设计模式,允许在运行时根据不同情况选择不同的算法或行为,极大地提高了代码的灵活性和可维护性。通过实例分析,本文将展示如何在PHP项目中有效利用策略模式来解决实际问题,并提升代码质量。
|
3月前
|
XML 存储 JavaScript
xml介绍与解析,及xml库包使用
xml介绍与解析,及xml库包使用
34 0
|
3月前
|
机器学习/深度学习 计算机视觉 Python
深度学习项目中在yaml文件中定义配置,以及使用的python的PyYAML库包读取解析yaml配置文件
深度学习项目中在yaml文件中定义配置,以及使用的python的PyYAML库包读取解析yaml配置文件
102 0
|
3月前
|
测试技术 API Android开发
Android经典实战之简化 Android 相机开发:CameraX 库的全面解析
CameraX是Android Jetpack的一个组件,旨在简化相机应用开发,提供了易于使用的API并支持从Android 5.0(API级别21)起的设备。其主要特性包括广泛的设备兼容性、简洁的API、生命周期感知、简化实现及方便的集成与测试。通过简单的几个步骤即可实现如拍照、视频录制等功能。此外,还提供了最佳实践指导以确保应用的稳定性和性能。
66 0
|
3月前
|
JSON 数据格式
【Axure高手秘籍】掌握这招,让你的原型设计效率飙升!——元件库导入与使用教程及主流资源下载全解析
【8月更文挑战第20天】Axure RP是界面设计与交互原型制作的强大工具。掌握元件库能显著提升设计效率。元件库包含预设UI元素如按钮、表单等,可直接拖放构建布局。在Axure RP中,通过“元件”选项下的“库”可访问并导入新元件库。导入后,轻松拖放元件至画布调整,甚至自定义样式和交互。利用脚本还能模拟真实交互效果,如按钮点击反馈。推荐资源包括Axure Marketplace、UIZilla等,助力高效设计。
74 0
|
3月前
Google Guava ListeningExecutorService
Google Guava ListeningExecutorService
20 0

推荐镜像

更多