动手实践:零基础学Java | 数组

8 阅读7分钟

动手实践

实现一个方法,统计数组中每个元素出现的次数(不考虑复杂情况)。

基于目前学习的知识,采用先排序后统计的方法。


思路分析

  1. 排序:将数组复制一份并排序(使用 Arrays.sort())。排序后相同的元素会相邻。
  2. 遍历统计:遍历排序后的数组,用两个变量记录当前元素和出现次数。当元素变化时,输出上一个元素的统计结果。
  3. 输出:遍历结束后输出最后一个元素的统计结果。

代码实现

import java.util.Arrays;

public class ArrayStats {

    /**
     * 统计 int 数组中每个元素出现的次数(不考虑复杂情况,使用排序法)
     * @param arr 待统计的数组
     */
    public static void countElements(int[] arr) {
        // 处理空数组或 null
        if (arr == null || arr.length == 0) {
            System.out.println("数组为空,无法统计");
            return;
        }

        // 1. 复制并排序(避免修改原数组)
        int[] sorted = Arrays.copyOf(arr, arr.length);
        Arrays.sort(sorted);

        // 2. 初始化当前元素和计数器
        int current = sorted[0];
        int count = 1;

        // 3. 从第二个元素开始遍历
        for (int i = 1; i < sorted.length; i++) {
            if (sorted[i] == current) {
                // 相同元素,计数加1
                count++;
            } else {
                // 元素变化,输出上一个元素的统计结果
                System.out.println(current + " 出现 " + count + " 次");
                // 更新为新的元素
                current = sorted[i];
                count = 1;
            }
        }
        // 4. 输出最后一个元素的统计结果
        System.out.println(current + " 出现 " + count + " 次");
    }

    public static void main(String[] args) {
        int[] scores = {95, 87, 76, 95, 92, 87, 87, 76, 100};
        countElements(scores);
    }
}

运行结果

76 出现 2 次
87 出现 3 次
92 出现 1 次
95 出现 2 次
100 出现 1 次

方法说明

  • 为什么复制数组?
    为了避免排序操作修改原数组,我们用 Arrays.copyOf() 创建了一个副本。如果允许修改原数组,也可以直接对原数组排序,但通常我们希望保持原数据不变。

  • 为什么使用排序?
    排序后相同元素相邻,只需一次遍历即可完成统计,时间复杂度为 O(n log n)(排序占主导)。如果元素范围已知且较小(如成绩0~100),可以用一个长度为101的计数数组,时间复杂度 O(n),但通用性较差。

  • 能否统计其他类型?
    对于 double[]String[] 等,思路相同,但比较相等时需注意浮点精度或使用 equals()。这里以 int 为例,简单直观。


扩展思考

如果希望统计结果能继续在程序中使用(而不是仅仅打印),可以返回一个二维数组或自定义对象。例如,可以设计一个方法返回两个数组:一个存放不重复的元素,一个存放对应的次数。但在基础阶段,打印输出已经足够理解原理。

如果你有兴趣,可以尝试用双重循环实现(不排序),但需要注意避免重复统计。这种方式效率较低(O(n²)),但也是锻炼逻辑的好方法。

动手试试:修改代码,让方法返回一个 String 描述结果,而不是直接打印。

实现数组的“去重”操作,返回新数组。

使用二维数组打印杨辉三角前10行。

完善上面的成绩管理系统,增加删除和修改功能。

在上一版本的成绩管理系统中,我们实现了添加成绩、显示成绩、统计信息、排序显示和查找功能。现在,我们在此基础上增加删除修改功能,让系统更加完整实用。


1. 需求分析

  • 删除成绩:用户可以通过输入学生序号(第几个学生)来删除该学生的成绩。删除后,后续学生成绩自动前移,总人数减1。
  • 修改成绩:用户可以通过输入学生序号,将该学生的成绩修改为新的值。

为了与现有菜单风格一致,我们将新增两个菜单选项:

  • 6. 删除成绩
    1. 修改成绩
    1. 退出(原6改为8)

2. 实现思路

2.1 删除功能
  • 接收用户输入的学生序号(1-based),转换为数组索引(0-based)。
  • 检查索引有效性(必须在0到count-1之间)。
  • 将索引后面的所有元素向前移动一位(使用System.arraycopy)。
  • count减1。
2.2 修改功能
  • 接收用户输入的学生序号(1-based),转换为数组索引(0-based)。
  • 检查索引有效性。
  • 接收新的成绩值,直接赋值给scores[index]
2.3 注意点
  • 删除和修改前需判断是否有成绩(count>0)。
  • 删除后,如果数组容量远大于实际元素个数,可以考虑缩容(暂不实现,保持简单)。
  • 用户输入非数字时,当前程序未做异常处理,可后续完善。

3. 代码实现

下面是修改后的完整ScoreManager类,新增了deleteScoremodifyScore方法,并更新了主菜单和case分支。

import java.util.Arrays;
import java.util.Scanner;

public class ScoreManager {
    private int[] scores;      // 存储成绩
    private int count;         // 实际学生数
    private static final int INIT_CAPACITY = 5;

    public ScoreManager() {
        scores = new int[INIT_CAPACITY];
        count = 0;
    }

    public ScoreManager(int initialCapacity) {
        if (initialCapacity < 0) {
            throw new IllegalArgumentException("容量不能为负数");
        }
        scores = new int[initialCapacity];
        count = 0;
    }

    // 添加成绩
    public void addScore(int score) {
        if (count == scores.length) {
            // 扩容为2倍
            scores = Arrays.copyOf(scores, scores.length * 2);
        }
        scores[count++] = score;
        System.out.println("添加成功!");
    }

    // 显示所有成绩
    public void displayScores() {
        if (count == 0) {
            System.out.println("暂无成绩");
            return;
        }
        System.out.println("成绩列表:");
        for (int i = 0; i < count; i++) {
            System.out.println("学生" + (i + 1) + ":" + scores[i]);
        }
    }

    // 统计信息
    public void statistics() {
        if (count == 0) {
            System.out.println("暂无成绩");
            return;
        }
        int sum = 0;
        int max = scores[0];
        int min = scores[0];
        for (int i = 0; i < count; i++) {
            sum += scores[i];
            if (scores[i] > max) max = scores[i];
            if (scores[i] < min) min = scores[i];
        }
        double avg = (double) sum / count;
        System.out.println("总人数:" + count);
        System.out.println("总分:" + sum);
        System.out.println("平均分:" + String.format("%.2f", avg));
        System.out.println("最高分:" + max);
        System.out.println("最低分:" + min);
    }

    // 排序并显示
    public void sortAndDisplay() {
        if (count == 0) {
            System.out.println("暂无成绩");
            return;
        }
        int[] temp = Arrays.copyOf(scores, count);
        Arrays.sort(temp);
        System.out.println("成绩排序(升序):" + Arrays.toString(temp));
    }

    // 查找成绩
    public void searchScore(int score) {
        if (count == 0) {
            System.out.println("暂无成绩");
            return;
        }
        boolean found = false;
        for (int i = 0; i < count; i++) {
            if (scores[i] == score) {
                System.out.println("成绩 " + score + " 出现在第 " + (i + 1) + " 位学生");
                found = true;
            }
        }
        if (!found) {
            System.out.println("未找到该成绩");
        }
    }

    // 删除成绩:按学生序号(1-based)
    public void deleteScore(int studentNo) {
        if (count == 0) {
            System.out.println("暂无成绩,无法删除");
            return;
        }
        int index = studentNo - 1;  // 转换为0-based索引
        if (index < 0 || index >= count) {
            System.out.println("学生序号无效,请输入1~" + count + "之间的数字");
            return;
        }
        int deletedScore = scores[index];
        // 将 index+1 及之后的元素前移一位
        System.arraycopy(scores, index + 1, scores, index, count - index - 1);
        count--;
        // 可选:将原最后一个位置置0(清理引用,但int类型不需要)
        // scores[count] = 0; // 可省略
        System.out.println("已删除第 " + studentNo + " 位学生的成绩:" + deletedScore);
    }

    // 修改成绩:按学生序号(1-based)和新成绩
    public void modifyScore(int studentNo, int newScore) {
        if (count == 0) {
            System.out.println("暂无成绩,无法修改");
            return;
        }
        int index = studentNo - 1;
        if (index < 0 || index >= count) {
            System.out.println("学生序号无效,请输入1~" + count + "之间的数字");
            return;
        }
        int oldScore = scores[index];
        scores[index] = newScore;
        System.out.println("已修改第 " + studentNo + " 位学生的成绩:从 " + oldScore + " 改为 " + newScore);
    }

    public static void main(String[] args) {
        ScoreManager manager = new ScoreManager();
        Scanner scanner = new Scanner(System.in);

        while (true) {
            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("7. 修改成绩");
            System.out.println("8. 退出");
            System.out.print("请选择:");

            int choice;
            try {
                choice = scanner.nextInt();
            } catch (Exception e) {
                System.out.println("输入错误,请输入数字!");
                scanner.next(); // 清除错误输入
                continue;
            }

            if (choice == 8) {
                System.out.println("感谢使用!");
                break;
            }

            switch (choice) {
                case 1:
                    System.out.print("请输入成绩:");
                    int score = scanner.nextInt();
                    manager.addScore(score);
                    break;
                case 2:
                    manager.displayScores();
                    break;
                case 3:
                    manager.statistics();
                    break;
                case 4:
                    manager.sortAndDisplay();
                    break;
                case 5:
                    System.out.print("请输入要查找的成绩:");
                    int target = scanner.nextInt();
                    manager.searchScore(target);
                    break;
                case 6:
                    System.out.print("请输入要删除的学生序号:");
                    int delNo = scanner.nextInt();
                    manager.deleteScore(delNo);
                    break;
                case 7:
                    System.out.print("请输入要修改的学生序号:");
                    int modNo = scanner.nextInt();
                    System.out.print("请输入新的成绩:");
                    int newScore = scanner.nextInt();
                    manager.modifyScore(modNo, newScore);
                    break;
                default:
                    System.out.println("无效选择,请输入1~8之间的数字");
            }
        }
        scanner.close();
    }
}

4. 关键点解释

  • 索引转换:用户看到的是从1开始的序号,内部数组从0开始,所以需要减1转换。
  • 删除时元素前移:使用System.arraycopy(scores, index+1, scores, index, count-index-1),将删除位置后面的所有元素复制到当前位置。
  • 修改时直接赋值:通过索引修改原数组。
  • 边界检查:确保用户输入的序号在有效范围内(1~count),否则给出友好提示。

5. 扩展思考

  • 删除时是否要缩容?如果删除后数组容量远大于实际元素个数,可以考虑缩容以节省内存,但简单系统可不实现。
  • 按成绩值删除:可以增加按成绩值删除所有匹配项的功能,但需考虑多个相同成绩的情况。
  • 异常处理:当前仅处理了输入非数字的情况,还可处理负数成绩等业务规则。