You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java快速统计List中包含的三元组数量——性能优化及多线程咨询

优化方案:从数据结构到多线程的全流程优化

1. 单线程核心优化:用高效数据结构替代线性查找

你的三重循环最大的问题是内层遍历整个数字组查找元素,时间复杂度是O(M3N)(M是三元组数量,N是数字组长度)。换成以下两种数据结构,能把单个元素的查找时间从O(N)降到O(1):

方案1:HashSet(通用场景)

把数字组转换成HashSet,直接判断三元组的三个元素是否都在集合中:

Set<Integer> zahlenSet = new HashSet<>(zahlen);
int treffer = 0;

for(List<Integer> dreier : dreierKombis) {
    if(zahlenSet.contains(dreier.get(0)) && 
       zahlenSet.contains(dreier.get(1)) && 
       zahlenSet.contains(dreier.get(2))) {
        treffer++;
    }
}

这个修改能把单三元组的检查时间从O(N)降到O(1),对于你的场景,效率至少提升6倍(因为数字组长度是6)。

方案2:BitSet(针对1-42的乐透场景)

由于乐透数字范围固定是1-42,BitSet是更高效的选择——占用空间极小(仅42位=5字节),且检查操作是原生位运算,速度比HashSet更快:

BitSet zahlenBitSet = new BitSet(43); // 数字范围1-42,索引从1开始
for(int num : zahlen) {
    zahlenBitSet.set(num);
}
int treffer = 0;

for(List<Integer> dreier : dreierKombis) {
    if(zahlenBitSet.get(dreier.get(0)) && 
       zahlenBitSet.get(dreier.get(1)) && 
       zahlenBitSet.get(dreier.get(2))) {
        treffer++;
    }
}

这种方式比HashSet的效率还要高30%-50%,非常适配你的乐透数字范围。

2. 多线程优化:拆分任务并行处理

当单线程优化后仍不够时,可以通过多线程利用CPU多核资源,核心是任务拆分:

拆分方式选择

根据你的数据规模(1.5亿三元组 + 1000个数字组),有两种拆分思路:

  • 按数字组分拆:每个线程处理一个或多个数字组,对所有1.5亿三元组进行检查。适合数字组数量较多的场景。
  • 按三元组分拆:把1.5亿三元组分成若干块,每个线程处理一块三元组,对所有1000个数字组进行检查。适合三元组数量极大的场景。

示例代码(按数字组分拆的线程池实现)

// 初始化线程池,核心线程数设为CPU核心数(比如8核就设8)
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
List<Future<Integer>> futures = new ArrayList<>();

// 预处理所有数字组为BitSet
List<BitSet> zahlenBitSets = new ArrayList<>();
for(List<Integer> zahlen : alleZahlenGruppen) { // alleZahlenGruppen是你的1000个数字组
    BitSet bs = new BitSet(43);
    for(int num : zahlen) bs.set(num);
    zahlenBitSets.add(bs);
}

// 提交任务:每个线程处理一个数字组的检查
for(BitSet bs : zahlenBitSets) {
    futures.add(executor.submit(() -> {
        int count = 0;
        for(List<Integer> dreier : dreierKombis) {
            if(bs.get(dreier.get(0)) && bs.get(dreier.get(1)) && bs.get(dreier.get(2))) {
                count++;
            }
        }
        return count;
    }));
}

// 汇总所有结果
int totalTreffer = 0;
for(Future<Integer> future : futures) {
    totalTreffer += future.get();
}

executor.shutdown();

注意事项

  • 不要手动创建线程,用ExecutorService线程池管理,避免线程创建销毁的开销。
  • 如果三元组数据量太大(1.5亿),可以把三元组分块后存入内存或者磁盘,每个线程读取一块处理,避免内存溢出。
  • 乐透场景下,可以提前把每个三元组转换成排序后的整数数组或自定义不可变类,避免重复排序,进一步提升效率。

3. 进阶优化:预处理历史开奖的三元组集合

根据你的核心背景,其实可以换个思路:每个历史开奖号码的三元组只有20个(C(6,3)=20),把所有历史开奖的三元组合并成一个全局HashSet(去重后最多100020=20000个),然后直接统计1.5亿个组合三元组中有多少在这个全局Set里。这种方式的时间复杂度是O(M)(M是三元组数量),比原来的O(MK)(K是数字组数量)效率提升几个数量级:

// 预处理所有历史开奖的三元组,存入全局Set
Set<String> historicalTriples = new HashSet<>();
for(List<Integer> ziehung : alleHistorischenZiehungen) { // 所有历史开奖号码
    // 生成该开奖号码的所有三元组
    List<List<Integer>> triples = generateTriples(ziehung);
    // 把三元组排序后转成字符串当key(避免顺序不同的重复三元组,比如[1,2,3]和[3,2,1]视为同一个)
    for(List<Integer> t : triples) {
        Collections.sort(t);
        historicalTriples.add(t.toString());
    }
}

// 统计目标三元组中在全局Set里的数量
int treffer = 0;
for(List<Integer> dreier : dreierKombis) {
    Collections.sort(dreier);
    if(historicalTriples.contains(dreier.toString())) {
        treffer++;
    }
}

这种方式完全规避了对每个数字组遍历所有三元组的操作,直接把问题转换成集合查找,效率提升非常明显。


内容的提问来源于stack exchange,提问作者Gaukler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 17:33:04