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

Java算法优化问询:基于HashMap<List>获取Top10热门邮政编码

问题描述

现有一个存储邮政编码与对应公司列表的HashMap:

postcodeToCompaniesList = new HashMap<String, List<Company>>();

该Map包含数千个邮政编码条目,每个邮编对应的公司列表至少有1个Company对象。当前需求是获取公司数量最多的前10个热门邮政编码,并按格式 "Postcode SW1A 0AA has 50 companies" 输出。

当前实现通过自定义Record类存储邮编和对应公司数,再用RecordList维护前10条数据,但每次添加元素后都要执行排序操作,效率偏低,希望找到更高效、优雅的实现方式(比如利用Java Stream)。

现有实现代码如下:

自定义Record与RecordList类

final class Record implements Comparable<Record> {
    public String postcode;
    public int count;

    public Record(String postcode, int count){
        this.postcode = postcode;
        this.count = count;
    }

    @Override 
    public int compareTo(Record r){
        return Integer.valueOf(this.count).compareTo(Integer.valueOf(r.count));
    }
}

final class RecordList{
    List<Record> top10 = new ArrayList<Record>();
    
    public void addRecord(Record record){
        if (this.top10.size() < 10){
            this.top10.add(record);
            Collections.sort(this.top10);
        } else if (record.count > this.top10.get(0).count){
            this.top10.add(record);
            Collections.sort(this.top10);
            this.top10.remove(0);
        }
    }
}

遍历处理代码

RecordList recordList = new RecordList();
Set<String> postcodes = this.postcodeToCompaniesList.keySet();

for(String postcode : postcodes){
  int companyCount = this.postcodeToCompaniesList.get(postcode).size();
  recordList.addRecord(new Record(postcode, companyCount));
}

System.out.println(gson.toJson(recordList));

优化方案

方案一:Java Stream API(简洁优雅)

Stream API可以用链式调用完成转换、排序、取前10的操作,代码简洁易读,对于数千条数据的规模,性能完全满足需求:

postcodeToCompaniesList.entrySet()
    // 将Map条目转换为Record对象
    .stream()
    .map(entry -> new Record(entry.getKey(), entry.getValue().size()))
    // 按公司数量降序排序(若数量相同,可追加邮编排序逻辑)
    .sorted((r1, r2) -> Integer.compare(r2.count, r1.count))
    // 取前10个结果
    .limit(10)
    // 按指定格式输出
    .forEach(record -> System.out.printf("Postcode %s has %d companies%n", record.postcode, record.count));

如果不想依赖自定义Record类,可直接使用AbstractMap.SimpleEntry替代,省去类定义的麻烦:

postcodeToCompaniesList.entrySet()
    .stream()
    .map(entry -> new AbstractMap.SimpleEntry<>(entry.getKey(), entry.getValue().size()))
    .sorted((e1, e2) -> Integer.compare(e2.getValue(), e1.getValue()))
    .limit(10)
    .forEach(e -> System.out.printf("Postcode %s has %d companies%n", e.getKey(), e.getValue()));

方案二:优先队列(PriorityQueue)优化性能

如果数据量远大于数千条,优先队列(最小堆)可以避免全量排序,仅维护大小为10的堆结构,每次插入/替换操作的时间复杂度为O(log10),整体效率更高:

// 初始化最小堆:堆顶元素是当前前10中公司数量最少的条目
PriorityQueue<Record> minHeap = new PriorityQueue<>(Comparator.comparingInt(r -> r.count));

for (Map.Entry<String, List<Company>> entry : postcodeToCompaniesList.entrySet()) {
    int companyCount = entry.getValue().size();
    Record record = new Record(entry.getKey(), companyCount);
    
    if (minHeap.size() < 10) {
        minHeap.offer(record);
    } else if (companyCount > minHeap.peek().count) {
        minHeap.poll(); // 移除堆顶的最小元素
        minHeap.offer(record);
    }
}

// 将堆中元素转换为列表并按公司数量降序排序后输出
List<Record> top10Records = new ArrayList<>(minHeap);
top10Records.sort((r1, r2) -> Integer.compare(r2.count, r1.count));
top10Records.forEach(record -> System.out.printf("Postcode %s has %d companies%n", record.postcode, record.count));

原实现效率低的原因

原RecordList每次添加元素后都调用Collections.sort(top10),尽管列表大小最多为10,但数千次循环下来,总开销为O(n * 10 log10)。优先队列方案的总开销为O(n log10),与原实现开销接近但代码更简洁;Stream方案的全量排序开销为O(n logn),对于数千条数据来说,实际运行时间差异可忽略,但代码最简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 09:25:24