提升编程效率的API利器:精通Google Guava库区间范围映射RangeMap

文摘   2024-09-21 19:49   广东  

开发中处理各种数据范围和区间是一个常见的需求。Google的Guava库为我们提供了一个强大的工具——RangeMap,用于处理这种基于范围的映射问题。

一、RangeMap介绍

RangeMap是Guava提供的一种特殊的映射结构,它将不相交、且不为空的Range(范围)映射到一个特定的值。与传统Map不同,RangeMap的键是一个范围而不是单个元素。这种映射关系使得RangeMap在处理需要根据不同的范围来确定不同的行为或结果的问题时非常有用。它建立了区间与特定值之间的映射关系。当在已有映射的区间中插入相交的新区间时,相交部分的值会被新值覆盖,同时原区间会被拆分。此外,RangeMap不提供补集操作的功能。

二、RangeMap的应用场景

  • 重试机制: 使用RangeMap实现基于时间的重试机制。将时间范围映射到不同的重试策略上,可以灵活地控制重试行为。

  • 阶梯式收费: 在计费系统中,RangeMap可以方便地实现阶梯式收费模型。将不同的使用量或消费金额范围映射到不同的费用上,可以简化计费逻辑。

  • 配置管理: 根据不同的配置项来确定不同的行为。使用RangeMap管理这些配置项,可以将配置项的范围映射到对应的行为上,提高配置管理的灵活性。

三、RangeMap的核心特性

  • 不合并相邻的映射:RangeMap从不自动合并相邻的范围,即使这些相邻的范围映射到相同的值。这意味着每个范围都是独立且不相交的。

  • 基于红黑树的实现:TreeRangeMap是基于红黑树实现的,这种数据结构保证了RangeMap能够提供高效的范围查找和插入操作。

  • 灵活的范围定义:RangeMap支持各种范围定义,包括开区间、闭区间、半开半闭区间等。

  • 插入重叠区间的行为:插入一个与已保存区间发生重叠的新区间时,TreeRangeMap会采取以下行为:

    • 切割原有区间:为了确保每个区间都是互不重叠的,TreeRangeMap会对与新区间重叠的已保存区间进行切割。这意味着原有的区间会被分割成多个小区间。
    • 保留插入区间的完整性:切割操作会确保您正在插入的新区间保持完整,不会被分割成多个部分。
  • 通过单个值K查询:TreeRangeMap以区间作为键来存储数据,但可通过单K来查询它。调用get方法时使用其内部的红黑树结构来高效地查找包含给定值K的区间。由于区间是排序的,并且树结构允许快速查找,因此这个过程通常是相当高效的。

四、使用RangeMap

常用方法:

使用:

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(04), "Low");  
        rangeMap.put(Range.open(510), "Medium");  
        rangeMap.put(Range.closedOpen(1015), "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(612));  
        System.out.println(subRangeMap.asMapOfRanges());  // 输出: {(6, 10) => Medium, [10, 12) => High}  
  
        // asMapOfRanges方法获取整 RangeMap的视图  
        System.out.println(rangeMap.asMapOfRanges());  
  
        // 使用remove方法移除映射关系  
        rangeMap.remove(Range.closed(04));  
        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落在哪个区间而返回相应的值,或者如果没有区间包含该键则返回null。asMapOfRanges()方法返回一个视图,它展示了RangeMap中的所有映射关系。

再看下重叠区间的行为

        // 创建TreeRangeMap  
        RangeMap<Integer, String> rangeMap = TreeRangeMap.create();  
  
        // 向 RangeMap 添加映射关系  
        rangeMap.put(Range.closed(04), "Low");  
        rangeMap.put(Range.open(510), "Medium");  
        rangeMap.put(Range.closedOpen(1015), "High");  
        rangeMap.put(Range.greaterThan(15), "Very High");  
  
        // 插入重叠区间的场景  
        // 插入一个与现有区间重叠的新区间 [3, 8]  
        rangeMap.put(Range.closedOpen(38), "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(212);  
        RangeMap<Integer, String> spannedRangeMap = rangeMap.span(queryRange);    
        System.out.println(spannedRangeMap.asMapOfRanges());  
        // 输出: {(2, 3]=Low, [3, 8)=Overlap, [8, 10)=Medium, [10, 12)=High}  
  
        // lowerEndpoint和upperEndpoint获取边界  
        System.out.println("lower endpoint of the query range: " + queryRange.lowerEndpoint()); 
        // 输出 2  
        System.out.println("upper endpoint of the query range: " + queryRange.upperEndpoint()); 
        // 输出 12  
    }  

太强 ! SpringBoot中出入参增强的5种方法 : 加解密、脱敏、格式转换、时间时区处理

太强 ! SpringBoot中优化if-else语句的七种绝佳方法实战
SpringBoot使用EasyExcel并行导出多个excel文件并压缩zip下载
经典八股:Redis数据结构与底层实现揭秘
玩转同步控制:LockSupport深入解读
90分掌握一门语言:lua脚本基础到高级教程
提升编程效率的利器: Google Guava库中双向映射BitMap
从MySQL行格式原理看:为什么开发规范中不推荐NULL?数据是如何在磁盘上存储的?
SpringBoot中使用Jackson实现自定义序列化和反序列化控制的5种方式总结
提升编程效率的利器: Google Guava库之RateLimiter优雅限流
深入JVM逃逸分析原理:且看其如何提高程序性能和内存利用率
必知必会!MySQL索引下推:原理与实战

深入解析JVM内存分配优化技术:TLAB

SpringBoot中基于JWT的双token(access_token+refresh_token)授权和续期方案
SpringBoot中基于JWT的单token授权和续期方案
SpringBoot中Token登录授权、续期和主动终止的方案(Redis+Token)
微服务中token鉴权设计的4种方式总结

关注『 码到三十五 』,日有所获
                     点赞 和 在看 就是最大的支持

码到三十五
主要分享正经的开发技术(原理,架构,实践,源码等),以输出驱动输入;当然偶尔会穿插点生活琐碎,顺便吃个瓜,目的嘛,搞点精准流量,看能不能发发广告。
 最新文章