実践:コレクション応用

このレッスンはPhase 4のハンズオン実践で、学生成績管理システムを通じてコレクション知識を定着させます。

プロジェクト要件

学生成績管理システムを実装し、以下をサポート:

データモデル

JAVA
import java.util.ArrayList;
import java.util.List;

public class Student {
    private String id;
    private String name;
    private List<Subject> subjects;
    
    public Student(String id, String name) {
        this.id = id;
        this.name = name;
        this.subjects = new ArrayList<>();
    }
    
    public String getId() { return id; }
    public String getName() { return name; }
    public List<Subject> getSubjects() { return subjects; }
    
    public void addSubject(String name, int score) {
        subjects.add(new Subject(name, score));
    }
    
    public double getAverage() {
        if (subjects.isEmpty()) return 0;
        return subjects.stream()
            .mapToInt(Subject::getScore)
            .average()
            .orElse(0);
    }
    
    public int getTotal() {
        return subjects.stream()
            .mapToInt(Subject::getScore)
            .sum();
    }
    
    @Override
    public String toString() {
        return name + " (ID:" + id + ", 平均:" + String.format("%.1f", getAverage()) + ")";
    }
}

public class Subject {
    private String name;
    private int score;
    
    public Subject(String name, int score) {
        this.name = name;
        this.score = score;
    }
    
    public String getName() { return name; }
    public int getScore() { return score; }
    
    @Override
    public String toString() {
        return name + ":" + score;
    }
}

管理クラス

JAVA
import java.util.*;
import java.util.stream.Collectors;

public class GradeManager {
    private Map<String, Student> students;
    
    public GradeManager() {
        this.students = new LinkedHashMap<>();
    }
    
    // 学生を追加
    public void addStudent(Student student) {
        students.put(student.getId(), student);
        System.out.println("学生追加: " + student.getName());
    }
    
    // 成績を追加
    public void addScore(String studentId, String subject, int score) {
        Student student = students.get(studentId);
        if (student != null) {
            student.addSubject(subject, score);
            System.out.println(student.getName() + " " + subject + " " + score);
        } else {
            System.out.println("学生が見つかりません: " + studentId);
        }
    }
    
    // 学生を検索
    public Student findStudent(String id) {
        return students.get(id);
    }
    
    // 平均でソート
    public List<Student> sortByAverage() {
        return students.values().stream()
            .sorted((a, b) -> Double.compare(b.getAverage(), a.getAverage()))
            .collect(Collectors.toList());
    }
    
    // 合計でソート
    public List<Student> sortByTotal() {
        return students.values().stream()
            .sorted((a, b) -> Integer.compare(b.getTotal(), a.getTotal()))
            .collect(Collectors.toList());
    }
    
    // 平均スコアを計算
    public double getAverageScore() {
        return students.values().stream()
            .mapToDouble(Student::getAverage)
            .average()
            .orElse(0);
    }
    
    // トップ学生を取得
    public Student getTopStudent() {
        return students.values().stream()
            .max(Comparator.comparingDouble(Student::getAverage))
            .orElse(null);
    }
    
    // 最下位学生を取得
    public Student getBottomStudent() {
        return students.values().stream()
            .min(Comparator.comparingDouble(Student::getAverage))
            .orElse(null);
    }
    
    // 成績でグループ化
    public Map<String, List<Student>> groupByGrade() {
        return students.values().stream()
            .collect(Collectors.groupingBy(student -> {
                double avg = student.getAverage();
                if (avg >= 90) return "優秀";
                if (avg >= 80) return "良好";
                if (avg >= 70) return "平均";
                if (avg >= 60) return "及格";
                return "不及格";
            }));
    }
    
    // すべての学生を取得
    public List<Student> getAllStudents() {
        return new ArrayList<>(students.values());
    }
}

メインプログラム

JAVA
import java.util.List;
import java.util.Map;
import java.util.Scanner;

public class GradeSystem {
    private static GradeManager manager = new GradeManager();
    private static Scanner scanner = new Scanner(System.in);
    
    public static void main(String[] args) {
        // データ初期化
        initData();
        
        // メインループ
        int choice;
        do {
            showMenu();
            choice = scanner.nextInt();
            scanner.nextLine();
            
            switch (choice) {
                case 1: listStudents(); break;
                case 2: addScore(); break;
                case 3: viewStudent(); break;
                case 4: showRanking(); break;
                case 5: showStatistics(); break;
                case 6: showGroups(); break;
                case 0: System.out.println("さようなら!"); break;
                default: System.out.println("無効な選択");
            }
        } while (choice != 0);
        
        scanner.close();
    }
    
    private static void initData() {
        Student s1 = new Student("S001", "Alice");
        s1.addSubject("Java", 95);
        s1.addSubject("Python", 88);
        s1.addSubject("データベース", 92);
        
        Student s2 = new Student("S002", "Bob");
        s2.addSubject("Java", 85);
        s2.addSubject("Python", 90);
        s2.addSubject("データベース", 78);
        
        Student s3 = new Student("S003", "Charlie");
        s3.addSubject("Java", 78);
        s3.addSubject("Python", 82);
        s3.addSubject("データベース", 88);
        
        manager.addStudent(s1);
        manager.addStudent(s2);
        manager.addStudent(s3);
    }
    
    private static void showMenu() {
        System.out.println("\n=== 学生成績管理システム ===");
        System.out.println("1. 学生一覧");
        System.out.println("2. 成績追加");
        System.out.println("3. 学生詳細");
        System.out.println("4. 成績ランキング");
        System.out.println("5. 統計");
        System.out.println("6. 成績グループ");
        System.out.println("0. 終了");
        System.out.print("選択してください: ");
    }
    
    private static void listStudents() {
        System.out.println("\n--- 学生一覧 ---");
        for (Student s : manager.getAllStudents()) {
            System.out.println(s);
        }
    }
    
    private static void addScore() {
        System.out.print("学生IDを入力: ");
        String id = scanner.nextLine();
        System.out.print("科目を入力: ");
        String subject = scanner.nextLine();
        System.out.print("スコアを入力: ");
        int score = scanner.nextInt();
        scanner.nextLine();
        
        manager.addScore(id, subject, score);
    }
    
    private static void viewStudent() {
        System.out.print("学生IDを入力: ");
        String id = scanner.nextLine();
        
        Student student = manager.findStudent(id);
        if (student != null) {
            System.out.println("\n--- " + student.getName() + "の成績 ---");
            for (Subject s : student.getSubjects()) {
                System.out.println(s);
            }
            System.out.printf("合計: %d, 平均: %.1f%n", 
                student.getTotal(), student.getAverage());
        } else {
            System.out.println("学生が見つかりません");
        }
    }
    
    private static void showRanking() {
        System.out.println("\n--- 成績ランキング ---");
        List<Student> ranking = manager.sortByAverage();
        for (int i = 0; i < ranking.size(); i++) {
            Student s = ranking.get(i);
            System.out.printf("%d. %s - 平均: %.1f%n", 
                i + 1, s.getName(), s.getAverage());
        }
    }
    
    private static void showStatistics() {
        System.out.println("\n=== 統計 ===");
        System.out.printf("クラス平均: %.1f%n", manager.getAverageScore());
        
        Student top = manager.getTopStudent();
        if (top != null) {
            System.out.printf("最高: %s - %.1f%n", top.getName(), top.getAverage());
        }
        
        Student bottom = manager.getBottomStudent();
        if (bottom != null) {
            System.out.printf("最低: %s - %.1f%n", bottom.getName(), bottom.getAverage());
        }
    }
    
    private static void showGroups() {
        System.out.println("\n=== 成績グループ ===");
        Map<String, List<Student>> groups = manager.groupByGrade();
        
        groups.forEach((grade, students) -> {
            System.out.println("\n" + grade + " (" + students.size() + "人):");
            students.forEach(s -> 
                System.out.println("  " + s.getName() + " - " + String.format("%.1f", s.getAverage())));
        });
    }
}

出力例

TEXT
=== 学生成績管理システム ===
1. 学生一覧
2. 成績追加
3. 学生詳細
4. 成績ランキング
5. 統計
6. 成績グループ
0. 終了
選択してください: 4

--- 成績ランキング ---
1. Alice - 平均: 91.7
2. Bob - 平均: 84.3
3. Charlie - 平均: 82.7

設計のポイント

説明
Mapストレージ Mapで学生をIDでインデックス
Listストレージ Listで科目成績を格納
Stream操作 Streamでソート、統計、グループ化
Comparator カスタムソートルール
Collectors.groupingBy 成績レベルでグループ化

拡張機能

成績エクスポート

JAVA
public void exportToFile(String filename) {
    try (PrintWriter writer = new PrintWriter(new FileWriter(filename))) {
        writer.println("ID,名前,Java,Python,データベース,合計,平均");
        for (Student s : students.values()) {
            writer.printf("%s,%s,%s,%d,%.1f%n",
                s.getId(), s.getName(),
                s.getSubjects().stream()
                    .map(sub -> String.valueOf(sub.getScore()))
                    .collect(Collectors.joining(",")),
                s.getTotal(), s.getAverage());
        }
        System.out.println("エクスポート成功: " + filename);
    } catch (IOException e) {
        System.out.println("エクスポート失敗: " + e.getMessage());
    }
}

科目別統計

JAVA
public Map<String, Double> getSubjectAverages() {
    Map<String, List<Integer>> subjectScores = new HashMap<>();
    
    for (Student student : students.values()) {
        for (Subject subject : student.getSubjects()) {
            subjectScores.computeIfAbsent(subject.getName(), k -> new ArrayList<>())
                .add(subject.getScore());
        }
    }
    
    return subjectScores.entrySet().stream()
        .collect(Collectors.toMap(
            Map.Entry::getKey,
            e -> e.getValue().stream().mapToInt(Integer::intValue).average().orElse(0)
        ));
}

❓ よくある質問

Q 複数のクラスをサポートするにはどうすればいいですか?
A Map<String, GradeManager>を使用して複数のクラスを格納してください。
Q データを永続化するにはどうすればいいですか?
A ファイルIOまたはデータベースストレージを使用してください。
Q インポート機能をサポートするにはどうすればいいですか?
A CSVファイルを読み込み、解析してStudentオブジェクトを作成してください。

📖 まとめ

📝 演習

  1. インポート: CSVファイルから学生成績をインポート
  2. 科目別統計: 各科目の平均、最高、最低を計算
  3. 成績検索: 学生名によるあいまい検索をサポート

次のレッスン

次のレッスンでは、Phase 5に移り、ファイルIOを学びます — ファイル操作。

100%