使用Java 8 streams将HashSet按范围拆分到两个集合的最优方案
问题核心原因
HashSet 是无序集合,JDK 官方规范并未对它的遍历顺序做任何稳定保证,两次独立开启的 Stream 遍历顺序可能不一致,因此分开调用 limit() 和 skip() 会出现两次截取的元素不匹配的问题。
Java 8 Stream 解决方案
我们可以通过一次 Stream 遍历配合分区收集器完成拆分,避免两次遍历的顺序不一致问题:
- 如果需要按元素自然排序后再拆分(匹配你给出的示例效果),可以保留
sorted()操作 - 如果仅需要按 HashSet 当前遍历顺序拆分,去掉
sorted()即可
示例代码:
import java.util.Arrays; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; public class SplitSetDemo { public static void main(String[] args) { Set<Integer> set = new HashSet<>(Arrays.asList(1,2,3,4,5)); int n = 2; // 拆分阈值,前n个元素归入resultSet1 AtomicInteger counter = new AtomicInteger(0); Map<Boolean, Set<Integer>> splitResult = set.stream() .sorted() // 不需要排序可直接删除此行 .collect(Collectors.partitioningBy( element -> counter.getAndIncrement() < n, Collectors.toSet() )); Set<Integer> resultSet1 = splitResult.get(true); Set<Integer> resultSet2 = splitResult.get(false); System.out.println(resultSet1); // 输出 [1,2] System.out.println(resultSet2); // 输出 [3,4,5] } }
注意事项
- 上述方案默认使用串行流,若使用并行流会导致计数器计数混乱,不适用。
- 如果对拆分的顺序有明确要求,一定要加上排序操作,否则拆分结果依赖 HashSet 底层实现的遍历顺序,不同环境下结果可能不一致。
内容的提问来源于stack exchange,提问作者Mumzee
相关产品推荐
相关产品推荐

