如何在 Java 中从外部文件读取数据并按类别分组构建数组(或列表)

本文讲解如何从文本文件中读取拳击选手数据,按体重级别(如 flyweight、featherweight)分类,并分别提取各组最高分选手——使用 Java 8+ Stream API 和面向对象设计实现高效、可维护的解决方案。

本文讲解如何从文本文件中读取拳击选手数据,按体重级别(如 flyweight、featherweight)分类,并分别提取各组最高分选手——使用 Java 8+ Stream API 和面向对象设计实现高效、可维护的解决方案。

在实际开发中,从外部文件(如 boxingcompetition.txt)动态构建结构化数据是常见需求。与其手动维护多个独立数组(如 flyweightScores[] 和 featherweightScores[]),更推荐采用面向对象建模 + 集合分组 + 函数式处理的方式,兼顾可扩展性与代码清晰度。

核心思路

  1. 定义领域模型:创建 Boxer 类封装姓名、体重级别(division)、得分;
  2. 统一解析输入:逐行读取 CSV 格式数据,构造 Boxer 对象并存入 List<Boxer>;
  3. 按类别分组:利用 Collectors.groupingBy(Boxer::getDivision) 将选手按 division 聚合成 Map<String, List<Boxer>>;
  4. 每组取最优解:对每个体重级别的 List<Boxer> 调用 Collections.max() 获取最高分选手(需实现 Comparable 或传入 Comparator)。

关键实现细节

Boxer 必须可比较:compareTo() 方法应按 score 降序排列(返回 other.score - this.score 实现升序;当前示例中 this.score - other.score 实际为升序,但输出结果表明逻辑已适配最大值获取——更稳妥写法是显式使用 Comparator.comparingInt(Boxer::getScore).reversed()):

// 推荐写法:明确语义,避免歧义
@Override
public int compareTo(Boxer other) {
    return Integer.compare(other.score, this.score); // 降序:高分在前
}

健壮的文件读取:使用 getResourceAsStream() 加载 classpath 下资源(如 src/main/resources/boxingcompetition.txt),注意首行 competition 数值仅作提示,真实数据行数由文件内容决定,不应硬编码循环次数(原问题中 totalCompetition = competition * 2 易出错,应以 hasNextLine() 动态判断)。

分组后提取每类 Top 1

Map<String, List<Boxer>> grouped = boxers.stream()
    .collect(Collectors.groupingBy(Boxer::getDivision));

List<Boxer> topPerDivision = grouped.values().stream()
    .map(list -> list.stream()
        .max(Comparator.comparingInt(Boxer::getScore))
        .orElse(null)) // 处理空列表边界情况
    .filter(Objects::nonNull)
    .collect(Collectors.toList());

完整可运行示例(精简版)

import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;

public class BoxingAnalyzer {
    public static void main(String[] args) {
        List<Boxer> allBoxers = parseFile("/boxingcompetition.txt");

        // 按体重级别分组,并取每组最高分选手
        Map<String, Boxer> topByDivision = allBoxers.stream()
            .collect(Collectors.toMap(
                Boxer::getDivision,
                boxer -> boxer,
                (b1, b2) -> b1.getScore() >= b2.getScore() ? b1 : b2 // 同级别保留高分者
            ));

        topByDivision.forEach((div, boxer) ->
            System.out.printf("%s: %s (%d points)%n", div, boxer.getName(), boxer.getScore())
        );
    }

    private static List<Boxer> parseFile(String path) {
        List<Boxer> boxers = new ArrayList<>();
        try (Scanner scan = new Scanner(Objects.requireNonNull(
                BoxingAnalyzer.class.getResourceAsStream(path)))) {

            if (scan.hasNextLine()) scan.nextLine(); // 跳过首行 competition 计数(非必要)

            while (scan.hasNextLine()) {
                String line = scan.nextLine().trim();
                if (line.isEmpty()) continue;
                String[] parts = line.split("\\s*,\\s*"); // 更鲁棒的逗号分割(忽略空格)
                if (parts.length < 3) continue;
                boxers.add(new Boxer(
                    parts[0].trim(),
                    parts[2].trim(),
                    Integer.parseInt(parts[1].trim())
                ));
            }
        } catch (Exception e) {
            throw new RuntimeException("Failed to read boxing data", e);
        }
        return boxers;
    }

    private static class Boxer {
        private final String name, division;
        private final int score;

        Boxer(String name, String division, int score) {
            this.name = name;
            this.division = division;
            this.score = score;
        }

        String getName() { return name; }
        String getDivision() { return division; }
        int getScore() { return score; }

        @Override
        public String toString() {
            return String.format("%s (%s): %d", name, division, score);
        }
    }
}

注意事项

通过该方案,你不仅解决了“为不同级别创建数组”的表层需求,更建立了可复用的数据处理骨架——未来新增 lightweight 或 heavyweight 类别,代码零修改即可支持。

本文转载于:互联网 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。