提升编程效率的利器: 解析Google Guava库之集合篇RangeSet范围集合(五)

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

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


提升编程效率的利器: 解析Google Guava库之集合篇Immutable(一)

提升编程效率的利器: 解析Google Guava库之集合篇Multimap(二)

提升编程效率的利器: 解析Google Guava库之集合篇BitMap(三)

提升编程效率的利器: 解析Google Guava库之集合篇Table二维映射(四)

一、RangeSet简介

RangeSet是Guava库中的一个接口,它表示一组不重叠的、非空的范围集合。RangeSet中的每个范围都是一个Range对象,Range对象表示一个具有起始和结束边界的范围。RangeSet提供了一种方便的方式来管理和操作这些范围。

RangeSet还提供了丰富的查询和操作功能。例如,可以使用contains©方法查询给定的元素是否在RangeSet里,rangeContaining©方法返回包含给定元素的Range(如果不存在则返回null),以及encloses(Range)方法用来判断给定的Range是否包含在RangeSet里面。此外,span()方法返回一个包含在这个RangeSet的所有Range的并集。


要实现RangeSet接口,可以选择使用它的两个主要实现类:ImmutableRangeSet和TreeRangeSet。其中,ImmutableRangeSet是一个不可修改的RangeSet,而TreeRangeSet则是利用树的形式来实现,提供了高效的查询和插入操作。

二、RangeSet的核心特性

  • 自动合并范围:
    当向RangeSet中添加一个新的范围时,它会自动与现有的范围进行合并。如果新的范围与某个现有范围相交或相邻,它们会被合并成一个更大的范围。这种自动合并的特性使得RangeSet能够保持范围的不重叠性,从而简化了范围集合的管理。
  • 高效的查询操作:
    RangeSet提供了丰富的查询操作,可以快速地判断一个元素是否在某个范围内、获取包含某个元素的范围等。这些查询操作都是基于对范围树的高效遍历实现的,能够在对数时间内给出结果。
  • 灵活的范围操作:
    除了基本的查询操作外,RangeSet还支持各种范围操作,如并集、交集、差集等。这些操作可以方便地对范围集合进行组合和变换,满足各种复杂的需求。

三、RangeSet的实现原理

RangeSet的实现原理主要基于一种称为“范围树”的数据结构。范围树是一种平衡树,其中每个节点都表示一个范围。树中的节点按照范围的起始位置进行排序,以便快速查找和定位特定的范围。

当向RangeSet中添加一个新的范围时,它会遍历范围树,找到与新范围相交或相邻的现有范围,并进行合并。合并后的范围会被插入到树中的适当位置,以保持树的平衡性。这种合并和插入操作的时间复杂度都是对数级别的,因此RangeSet能够高效地处理大量的范围添加操作。


对于查询操作,RangeSet可以利用范围树的结构进行快速查找。例如,当查询一个元素是否包含在RangeSet中时,可以从树的根节点开始,沿着适当的分支向下遍历,直到找到一个包含该元素的范围或确定该元素不在RangeSet中。这种查询操作的时间复杂度也是对数级别的,因此RangeSet能够高效地处理大量的查询请求。

四、RangeSet的使用示例

使用 RangeSet 之前,我们得先了解一下 Guava 的Range 类,其实顾名思义就是表达区间范围,我们看一下它的 type 就明白了:

下面是一个使用RangeSet的简单示例,演示了如何创建一个RangeSet、向其中添加范围、并进行查询操作:

import com.google.common.collect.Range;  
import com.google.common.collect.RangeSet;  
import com.google.common.collect.TreeRangeSet;  
  
public class TreeRangeSetDemo {  
    public static void main(String[] args) {  
        // 创建一个空的TreeRangeSet  
        RangeSet<Integer> rangeSet = TreeRangeSet.create();  
  
        // 向RangeSet中添加几个不连续的范围  
        rangeSet.add(Range.closed(1, 3));     // [1, 3]  
        rangeSet.add(Range.open(5, 8));       // (5, 8)  
        rangeSet.add(Range.closedOpen(10, 12));// [10, 12)  
        rangeSet.add(Range.greaterThan(15));   // (15, +∞)  
  
        // 打印当前RangeSet的内容  
        System.out.println(rangeSet); // [1..3](5..8)[10..12)(15..+∞)  
  
        // 查询某个范围是否包含在RangeSet中  
        System.out.println(rangeSet.contains(Range.closed(2, 3)));   // true  
        System.out.println(rangeSet.contains(Range.open(6, 7)));     // true  
        System.out.println(rangeSet.contains(Range.closed(11, 11))); // true  
        System.out.println(rangeSet.contains(Range.closed(4, 5)));   // false  
  
        // 删除一个范围  
        rangeSet.remove(Range.open(5, 8));  
        System.out.println(rangeSet); // [1..3][10..12)(15..+∞)  
  
        // 获取与指定范围重叠的范围  
        RangeSet<Integer> overlappingRanges = rangeSet.subRangeSet(Range.atLeast(9));  
        System.out.println(overlappingRanges); // [10..12)(15..+∞)  
  
        // 获取指定范围的补集(这里仅展示与[0, 20]范围内的补集)  
        RangeSet<Integer> complement = rangeSet.complement().subRangeSet(Range.closed(0, 20));  
        System.out.println(complement); // (0..1)(3..5)(8..10)[12..15][15..20]  
        // 注意:由于complement()返回的是整个数域中不属于rangeSet的部分,  
        // 我们再次使用subRangeSet来限制补集的范围,以便更好地展示结果。  
  
        // 查询单个元素是否在RangeSet中  
        System.out.println(rangeSet.contains(2));    // true  
        System.out.println(rangeSet.contains(9));    // false  
  
        // 获取包含指定元素的范围  
        Range<Integer> rangeContaining11 = rangeSet.rangeContaining(11);  
        System.out.println(rangeContaining11); // [10..12)  
  
        Range<Integer> rangeContaining4 = rangeSet.rangeContaining(4);  
        System.out.println(rangeContaining4); // null,因为4不在rangeSet中  
  
        // 获取RangeSet的最小和最大元素(注意这不是一个Range,而是两个元素)  
        Integer minValue = rangeSet.asRanges().stream().map(Range::lowerEndpoint).min(Integer::compareTo).orElse(null);  
        Integer maxValue = rangeSet.asRanges().stream().map(Range::upperEndpoint).max(Integer::compareTo).orElse(null);  
        System.out.println("Min value: " + minValue); // Min value: 1  
        System.out.println("Max value: " + maxValue); // Max value: 2147483647 (Integer.MAX_VALUE,因为rangeSet包含(15..+∞))  
    }  
}

在这个例子中,我添加了一些不连续的整数范围,并进行了基本的操作,包括添加、删除范围、查询范围是否存在、获取范围的补集以及与指定范围重叠的范围等。我也演示了如何获取RangeSet中的最小和最大元素,尽管对于无限范围(15…+∞),最大值实际上是Integer.MAX_VALUE,因为TreeRangeSet内部使用Integer来表示范围,并且它会将这个无限范围视为上界为Integer.MAX_VALUE的范围。

请注意,在实际应用中,处理无限范围时应该特别小心,因为整数是有界的,而TreeRangeSet的某些操作可能会受到这个限制的影响。

再来看下 循环遍历 和 使用encloses方法检查范围包含关系 的示例:

        // 创建一个TreeRangeSet并添加一些不连续的范围  
        TreeRangeSet<Integer> rangeSet = TreeRangeSet.create();  
        rangeSet.add(Range.closed(1, 3));  
        rangeSet.add(Range.open(5, 8));  
        rangeSet.add(Range.closedOpen(10, 12));  
        rangeSet.add(Range.greaterThan(15));  
  
        // 使用encloses方法检查范围包含关系  
        boolean enclosesClosedRange = rangeSet.encloses(Range.closed(2, 3)); // true,因为[2,3]被[1,3]完全包含  
        boolean enclosesOpenRange = rangeSet.encloses(Range.open(6, 7)); // true,(6,7)被(5,8)完全包含  
        boolean enclosesSingletonRange = rangeSet.encloses(Range.singleton(11)); // true,11被[10,12)完全包含  
        boolean notEnclosesRange = rangeSet.encloses(Range.closed(4, 5)); // false,[4,5]不被任何范围完全包含  
  
        System.out.println("rangeSet.encloses(Range.closed(2, 3)): " + enclosesClosedRange);  
        System.out.println("rangeSet.encloses(Range.open(6, 7)): " + enclosesOpenRange);  
        System.out.println("rangeSet.encloses(Range.singleton(11)): " + enclosesSingletonRange);  
        System.out.println("rangeSet.encloses(Range.closed(4, 5)): " + notEnclosesRange);  
  
        // 遍历TreeRangeSet中的所有范围  
        System.out.println("Iterating over the ranges in the TreeRangeSet:");  
        Iterator<Range<Integer>> iterator = rangeSet.asRanges().iterator();  
        while (iterator.hasNext()) {  
            Range<Integer> range = iterator.next();  
            System.out.println(range);  
        }  
  
        // 使用增强的for循环遍历(更简洁)  
        System.out.println("Iterating over the ranges using enhanced for loop:");  
        for (Range<Integer> range : rangeSet.asRanges()) {  
            System.out.println(range);  
        }  
    }  

五、总结

Guava的RangeSet提供了一种高效、灵活的方式来处理范围集合。它基于范围树的数据结构,实现了范围的自动合并和高效的查询操作。通过RangeSet,我们可以方便地管理和操作各种范围集合,满足各种复杂的需求。在实际应用中,我们可以利用RangeSet来解决时间范围管理、数字范围限制等问题,提高代码的可读性和维护性。


相关文章
|
2月前
|
缓存 Java 调度
Java并发编程:深入解析线程池与Future任务
【7月更文挑战第9天】线程池和Future任务是Java并发编程中非常重要的概念。线程池通过重用线程减少了线程创建和销毁的开销,提高了资源利用率。而Future接口则提供了检查异步任务状态和获取任务结果的能力,使得异步编程更加灵活和强大。掌握这些概念,将有助于我们编写出更高效、更可靠的并发程序。
|
1月前
|
测试技术 开发者 Python
Python 编程中的装饰器深入解析
【8月更文挑战第1天】本文将通过实例和代码演示,深入探讨 Python 中装饰器的概念、用法和高级应用。我们将从基础开始,逐步过渡到如何自定义装饰器,并展示其在日志记录、性能测试等场景下的实际用途。文章最后还将讨论装饰器的常见误区和最佳实践。
|
15天前
|
SQL 设计模式 安全
Java编程中的单例模式深入解析
【8月更文挑战第27天】本文旨在探索Java中实现单例模式的多种方式,并分析其优缺点。我们将通过代码示例,展示如何在不同的场景下选择最合适的单例模式实现方法,以及如何避免常见的陷阱。
|
4天前
|
存储 JSON API
Python编程:解析HTTP请求返回的JSON数据
使用Python处理HTTP请求和解析JSON数据既直接又高效。`requests`库的简洁性和强大功能使得发送请求、接收和解析响应变得异常简单。以上步骤和示例提供了一个基础的框架,可以根据你的具体需求进行调整和扩展。通过合适的异常处理,你的代码将更加健壮和可靠,为用户提供更加流畅的体验。
21 0
|
11天前
|
数据库 Windows
超详细步骤解析:从零开始,手把手教你使用 Visual Studio 打造你的第一个 Windows Forms 应用程序,菜鸟也能轻松上手的编程入门指南来了!
【8月更文挑战第31天】创建你的第一个Windows Forms (WinForms) 应用程序是一个激动人心的过程,尤其适合编程新手。本指南将带你逐步完成一个简单WinForms 应用的开发。首先,在Visual Studio 中创建一个“Windows Forms App (.NET)”项目,命名为“我的第一个WinForms 应用”。接着,在空白窗体中添加一个按钮和一个标签控件,并设置按钮文本为“点击我”。然后,为按钮添加点击事件处理程序`button1_Click`,实现点击按钮后更新标签文本为“你好,你刚刚点击了按钮!”。
30 0
|
11天前
|
开发者 编解码
界面适应奥秘:从自适应布局到图片管理,Xamarin响应式设计全解析
【8月更文挑战第31天】在 Xamarin 的世界里,构建灵活且适应性强的界面是每位开发者的必修课。本文将带您探索 Xamarin 的响应式设计技巧,包括自适应布局、设备服务协商和高效图片管理,帮助您的应用在各种设备上表现出色。通过 Grid 和 StackLayout 实现弹性空间分配,利用 Device 类检测设备类型以加载最优布局,以及使用 Image 控件自动选择合适图片资源,让您轻松应对不同屏幕尺寸的挑战。掌握这些技巧,让您的应用在多变的市场中持续领先。
19 0
|
11天前
|
设计模式 安全 Java
Java编程中的单例模式深度解析
【8月更文挑战第31天】 单例模式,作为设计模式中的经典之一,在Java编程实践中扮演着重要的角色。本文将通过简洁易懂的语言,逐步引导读者理解单例模式的本质、实现方法及其在实际应用中的重要性。从基础概念出发,到代码示例,再到高级应用,我们将一起探索这一模式如何优雅地解决资源共享和性能优化的问题。
|
12天前
|
存储 开发者 Ruby
【揭秘Ruby高手秘籍】OOP编程精髓全解析:玩转类、继承与多态,成就编程大师之路!
【8月更文挑战第31天】面向对象编程(OOP)是通过“对象”来设计软件的编程范式。Ruby作为一种纯面向对象的语言,几乎所有事物都是对象。本文通过具体代码示例介绍了Ruby中OOP的核心概念,包括类与对象、继承、封装、多态及模块混合,展示了如何利用这些技术更好地组织和扩展代码。例如,通过定义类、继承关系及私有方法,可以创建具有特定行为的对象,并实现灵活的方法重写和功能扩展。掌握这些概念有助于提升代码质量和可维护性。
21 0
|
1月前
|
开发者 Python
Python编程中的装饰器深度解析
【8月更文挑战第2天】装饰器在Python中是一种强大的工具,它允许我们在不修改原函数代码的情况下增加函数的功能。本文将深入探讨Python装饰器的工作原理,并通过实际的代码示例展示如何创建和应用装饰器。我们将从基础的装饰器概念出发,逐步过渡到更复杂的使用场景,包括带参数的装饰器和嵌套装饰器。无论你是初学者还是有经验的开发者,这篇文章都将帮助你更好地理解和利用Python装饰器来提升你的代码效率和可读性。
13 1
|
2月前
|
存储 数据库 Android开发
🔥Android Jetpack全解析!拥抱Google官方库,让你的开发之旅更加顺畅无阻!🚀
【7月更文挑战第28天】在Android开发中追求高效稳定的路径?Android Jetpack作为Google官方库集合,是你的理想选择。它包含多个独立又协同工作的库,覆盖UI到安全性等多个领域,旨在减少样板代码,提高开发效率与应用质量。Jetpack核心组件如LiveData、ViewModel、Room等简化了数据绑定、状态保存及数据库操作。引入Jetpack只需在`build.gradle`中添加依赖。例如,使用Room进行数据库操作变得异常简单,从定义实体到实现CRUD操作,一切尽在掌握之中。拥抱Jetpack,提升开发效率,构建高质量应用!
49 4

热门文章

最新文章

推荐镜像

更多