Guava之RangeSet学习小结

简介

  RangeSet用来处理一系列不连续,非空的range。当添加一个range到一个RangeSet之后,任何有连续的range将被自动合并,而空的range将被自动去除。

  RangeSet类是一个接口,需要用它的子类来声明一个RangeSet型的对象,实现了RangeSet接口的类有ImmutableRangeSet和TreeRangeSet,ImmutableRangeSet是一个不可修改的RangeSet,而TreeRangeSet是利用树的形式来实现。

 

使用

        RangeSet<Integer> rangeSet = TreeRangeSet.create();
        rangeSet.add(Range.closed(1, 10));
        System.out.println("rangeSet:"+rangeSet);
        rangeSet.add(Range.closedOpen(11, 15));
        System.out.println("rangeSet:"+rangeSet);
        rangeSet.add(Range.open(15, 20));
        System.out.println("rangeSet:"+rangeSet);
        rangeSet.add(Range.openClosed(-1, 1));
        System.out.println("rangeSet:"+rangeSet);
        rangeSet.remove(Range.open(5, 10));
        System.out.println("rangeSet:"+rangeSet);

 结果

rangeSet:[[1‥10]]
rangeSet:[[1‥10], [11‥15)]
rangeSet:[[1‥10], [11‥15), (15‥20)]
rangeSet:[(-1‥10], [11‥15), (15‥20)]
rangeSet:[(-1‥5], [10‥10], [11‥15), (15‥20)]

 

 

遍历

Set ranges = rangeSet.asRanges();
        Iterator<Range> iterator = ranges.iterator();
        while (iterator.hasNext()){
            Range next = iterator.next();
            System.out.println(next);
        }

 

结果

(-1‥5]
[10‥10]
[11‥15)
(15‥20)

 

其他操作

 如果我们需要得到rangeSet互补的范围,我们可以用RangeSet提供的complement()方法。

RangeSet complement = rangeSet.complement();
 System.out.println(complement);
[(-∞‥-1], (5‥10), (10‥11), [15‥15], [20‥+∞)]

 

如果想知道某个元素是在rangeSet中哪个范围里面:

Range integerRange = rangeSet.rangeContaining(17);
System.out.println(integerRange);//输出(15‥20),因为17被包含在(15‥20)中,所以输出这个范围。

 

 

<think>好的,用户现在问的是如何将一个Java的Set分割成等量的多个Set。首先,我需要理解用户的需求。他们可能有一个较大的集合,想要分成几个较小的子集,每个子集的大小大致相等。这可能用于并行处理、分批处理数据或者资源分配等场景。 用户之前的问题是关于“2cc/日”的缩写解释,现在转向Java编程,说明用户可能在不同领域的问题中切换,或者他们的工作涉及多个领域,比如医疗和软件开发。这时候需要确保回答准确且易于理解,尤其是用户可能对Java集合操作不太熟悉。 接下来,我需要考虑如何分割Set。Java中的Set是无序的,并且不保证任何特定的顺序,所以分割的时候需要考虑这一点。常见的集合如HashSet、TreeSet,但TreeSet是有序的。用户可能希望保持元素分布的均匀,但如果是HashSet,元素顺序是不确定的,所以分割可能只是简单的均分,而不会考虑元素的顺序。如果是需要保持某种顺序,比如按自然顺序排列的TreeSet,可能需要不同的处理方式。 然后,分割的方法。常见的方法包括计算每个子集的大小,然后遍历原始集合,将元素逐个添加到子集中,直到达到每个子集的大小。这里需要注意的是,Set没有按索引访问的方法,所以必须通过迭代器来遍历元素。另外,Java 8的流和Guava库可能有现成的方法,但用户可能需要原生的Java实现,特别是如果他们不能使用第三方库的话。 还需要考虑集合大小无法被子集数量整除的情况。例如,10个元素的Set分成3个子集,可能的分配是4,3,3或者3,3,4。这时候需要决定如何处理余数,让前面的子集多一个元素,还是后面的。通常,前面的子集会多一个,这样处理起来更直观。 然后,用户可能需要的代码示例。要提供一个通用的方法,可能使用泛型,接受原始Set和分割数量,返回一个包含多个子集的列表。需要处理空集合、分割数量大于集合大小的情况,比如返回多个空集合或者尽可能分割。但根据用户的问题,可能不需要处理这些边界情况,但最好在示例中提到或者处理。 另外,考虑到Java集合的特性,子集可以是HashSet或者LinkedHashSet,如果需要保持顺序的话。但原Set如果是无序的,子集的顺序可能也不重要。用户可能更关注元素被平均分配到各个子集,而不关心顺序。 还需要注意线程安全的问题,如果原Set是并发修改的,但可能用户的问题不涉及多线程,所以暂时不需要考虑。 总结步骤: 1. 确定每个子集的大小,总元素数除以子集数量,处理余数。 2. 使用迭代器遍历原Set,逐个添加到子集中。 3. 当子集达到预定大小时,创建新的子集,直到所有元素分配完毕。 4. 返回所有子集的列表。 现在,需要将这些步骤转化为代码示例,并解释关键点。例如,使用ArrayList来存储子集,使用迭代器遍历,处理余数使前面的子集多一个元素。同时,说明可能的变体,如保持顺序或者处理空集合。 最后,确保代码示例正确,比如测试用例。例如,分割一个包含10个元素的Set为3个子集,得到4,3,3的分割方式。同时,提醒用户Set的无序性可能导致每次运行结果不同,但元素总数不变。 可能用户在实际应用中遇到问题,比如元素类型、性能问题,但根据问题描述,用户只需要基础的分割方法,所以保持示例简单明了即可。</think>在 Java 中,将一个 `Set` 分割成多个等量的子 `Set`(尽可能均分)可以通过以下步骤实现。这里以 **按数量均分** 为目标,提供一种通用方法,并附上代码示例: --- ### **实现思路** 1. **计算子集大小** - 根据原始集合的大小和需要分割的数量,计算每个子集的**基本大小**,并处理无法整除时的余数(将余数分配给前几个子集)。 2. **遍历原始集合** - 使用迭代器遍历原始 `Set`,将元素依次填充到多个子集合中,直到所有元素分配完毕。 --- ### **代码示例** ```java import java.util.*; import java.util.stream.IntStream; public class SetSplitter { /** * 将原始 Set 分割成多个子 Set,尽可能均分 * @param originalSet 原始集合 * @param chunkCount 需要分割的子集数量 * @return 包含多个子 Set 的列表 */ public static <T> List<Set<T>> splitSet(Set<T> originalSet, int chunkCount) { List<Set<T>> result = new ArrayList<>(); if (originalSet == null || originalSet.isEmpty() || chunkCount <= 0) { return result; } int totalSize = originalSet.size(); int baseChunkSize = totalSize / chunkCount; int remainder = totalSize % chunkCount; Iterator<T> iterator = originalSet.iterator(); for (int i = 0; i < chunkCount; i++) { Set<T> chunk = new HashSet<>(); int currentChunkSize = baseChunkSize + (i < remainder ? 1 : 0); // 填充当前子集 IntStream.range(0, currentChunkSize).forEach(j -> { if (iterator.hasNext()) { chunk.add(iterator.next()); } }); if (!chunk.isEmpty()) { result.add(chunk); } } return result; } // 测试代码 public static void main(String[] args) { Set<Integer> originalSet = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); int chunkCount = 3; List<Set<Integer>> subSets = splitSet(originalSet, chunkCount); subSets.forEach(subSet -> System.out.println("子集: " + subSet)); } } ``` --- ### **输出示例** ``` 子集: [1, 2, 3, 4] 子集: [5, 6, 7] 子集: [8, 9, 10] ``` --- ### **关键点说明** 1. **均分逻辑** - 若总元素数无法整除分割数量,前 `remainder` 个子集会多 1 个元素(例如 10 个元素分 3 份,结果为 `4, 3, 3`)。 2. **保持无序性** - 由于 `Set` 本身是无序的(如 `HashSet`),子集的元素分配顺序可能与预期不同。若需保持顺序,可改用 `LinkedHashSet`。 3. **性能与线程安全** - 此方法基于迭代器遍历,时间复杂度为 O(n)。若原始集合是线程不安全的(如 `HashSet`),需在并发场景下自行加锁。 --- ### **适用场景** - 批量任务的分片处理(如并行计算)。 - 数据分页或分批加载。 - 分布式系统中数据的分区存储。 可根据具体需求调整子集的实现类(如 `TreeSet` 或 `LinkedHashSet`)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值