代码仅供参考,并提供程序整体思路的基础解析,请勿直接引用。可通过咸鱼AI进行代码重构降重以及获取更详细的代码解析!

一.目的

1. 掌握 Java 中的数组定义、引用
2. 掌握一维数组的定义和使用
3. 了解 Java 多维数组构建和使用
4. 了解 Java 容器概念;
5. 掌握 List、Set 和 Map 类的操作;

二.内容

(一)数组的应用
数组可以看成是具有相同数据类型的数据元素的组合,对这些数据的统一管理。在java 中,数组被定义为对象;数组变量属于引用类型;数组中的元素相当于该对象的成员变量。数组中的元素可以是任何数据类型,包括基本数据类型和引用类型。

(1) 一维数组

/**

  • 实现双色球中机选一注功能:
  • 红球--01-33中的随机6个,不能重复
  • 蓝球--01-16中的随机1个 结果--6+1
  • 结果为前六个排序好的红球+蓝球 例如:4 7 15 18 203216* @author Administrator
  • */


    DoubleColorBall.java代码

package com.xianyu.shuzu;
import java.util.Arrays;
import java.util.Random;

public class DoubleColorBall {

    // 生成随机不重复的红球号码
    private static int[] generateRedBalls() {
        Random random = new Random();
        int[] redBalls = new int[6];
        for (int i = 0; i < redBalls.length; ) {
            int num = random.nextInt(33) + 1; // 生成1到33的随机数
            // 利用流检查重复
            if (Arrays.stream(redBalls, 0, i).noneMatch(x -> x == num)) {
                redBalls[i++] = num;
            }
        }
        Arrays.sort(redBalls); // 排序红球号码
        return redBalls;
    }

    // 生成随机蓝球号码
    private static int generateBlueBall() {
        return new Random().nextInt(16) + 1; // 生成1到16的随机数
    }

    // 打印双色球号码
    private static void printBalls(int[] redBalls, int blueBall) {
        for (int redBall : redBalls) {
            System.out.print(redBall + " ");
        }
        System.out.println(blueBall);
    }

    public static void main(String[] args) {
        int[] redBalls = generateRedBalls();
        int blueBall = generateBlueBall();
        printBalls(redBalls, blueBall);
    }
}




运行结果
java 数组与集合类 案例及解析



代码解析
程序运行的原理是基于随机数生成和数组操作来模拟生成双色球彩票的一组号码。双色球彩票由6个红球号码和1个蓝球号码组成,其中红球号码范围是1到33,蓝球号码范围是1到16。

  1. 随机数生成

    • 使用 Random 类生成随机数。红球号码通过 nextInt(33) + 1 生成1到33之间的随机整数,蓝球号码通过 nextInt(16) + 1 生成1到16之间的随机整数。
  2. 红球号码生成

    • 为了确保红球号码不重复,程序使用一个数组来存储已生成的红球号码。每次生成新的红球号码时,都会检查该号码是否已存在于数组中。如果不存在,则将其添加到数组中。
    • 使用Java 8的流(Stream)API来检查新号码是否与数组中已有的号码重复。noneMatch 方法会检查流中的元素是否都不满足给定条件(即新号码不等于任何已有号码)。
  3. 数组排序

    • 生成所有红球号码后,使用 Arrays.sort() 方法对数组进行排序,确保红球号码按升序排列。
  4. 蓝球号码生成

    • 蓝球号码的生成相对简单,因为它只需要一个不重复的号码,且范围较小(1到16),不需要检查重复。
  5. 打印号码

    • 最后,程序将生成的红球号码和蓝球号码打印出来。红球号码通过遍历数组打印,每个号码后跟一个空格;蓝球号码单独打印,并换行。

整个程序的运行原理就是通过随机数生成和数组操作来模拟双色球彩票的号码生成过程,并最终输出一组符合规则的彩票号码。



(2)二维与多维数组

/**

  • 编写程序:输入5个同学的数学、英语、Java、数据库、C语言五门课程的考试成绩
  • 求出每个学生的平均分,将成绩和平均分一起打印出来。* @author Administrator
  • */
    2测试样例:

    输入:
    78 88 56 69 36
    58 85 75 73 62
    78 45 69 63 56
    77 74 73 85 95
    45 75 62 64 68
    输出:
    数学 英语 Java 数据库 C语言 平均分
    78.0 88.0 56.0 69.0 36.0 65.4
    58.0 85.0 75.0 73.0 62.0 70.6
    78.0 45.0 69.0 63.0 56.0 62.2
    77.0 74.0 73.0 85.0 95.0 80.8
    45.0 75.0 62.0 64.0 68.0 62.8


StudentGrades.class代码

package com.xianyu.shuzu;
import java.util.Scanner;
public class StudentGrades {
    private static final int NUM_SUBJECTS = 5;
    private static final int NUM_STUDENTS = 5;

    // 新增一个方法来读取成绩
    private static void readGrades(double[][] grades, Scanner scanner) {
        for (int i = 0; i < NUM_STUDENTS; i++) {
            System.out.println("请输入第 " + (i + 1) + " 个同学的成绩(数学、英语、Java、数据库、C语言):");
            for (int j = 0; j < NUM_SUBJECTS; j++) {
                grades[i][j] = scanner.nextDouble();
            }
        }
    }

    // 新增一个方法来计算平均分并添加到成绩数组中
    private static void calculateAverages(double[][] gradesWithAverages) {
        for (int i = 0; i < NUM_STUDENTS; i++) {
            double sum = 0;
            for (int j = 0; j < NUM_SUBJECTS; j++) {
                sum += gradesWithAverages[i][j];
            }
            gradesWithAverages[i][NUM_SUBJECTS] = sum / NUM_SUBJECTS;
        }
    }

    // 新增一个方法来打印成绩和平均分
    private static void printGrades(double[][] gradesWithAverages) {
        System.out.println("数学 英语 Java 数据库 C语言 平均分");
        for (double[] studentGrades : gradesWithAverages) {
            for (int j = 0; j <= NUM_SUBJECTS; j++) {
                System.out.printf("%.1f ", studentGrades[j]);
            }
            System.out.println();
        }
    }

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

        double[][] gradesWithAverages = new double[NUM_STUDENTS][NUM_SUBJECTS + 1]; // 包含平均分的成绩数组

        readGrades(gradesWithAverages, scanner);
        calculateAverages(gradesWithAverages);
        printGrades(gradesWithAverages);

        scanner.close();
    }
}




运行结果
java 数组与集合类 案例及解析



代码解析
这个程序的目的是计算和显示5个学生的5门课程的成绩和平均分。它采用了模块化的方式设计,主要分为以下几个部分:

  1. 数据收集:首先,程序会提示并收集用户输入的每个学生的每门课程的分数。这些成绩会被存储在一个二维数组中,其中每行代表一个学生,每列代表一门科目的成绩。

  2. 平均分计算:之后,程序会计算每个学生的平均分。它通过将每个学生的所有科目成绩相加后再除以科目数量来得到。计算出的平均分将被添加到相应的成绩数组中。

  3. 数据展示:最后,程序会打印出每个学生的所有科目成绩和计算出的平均分。输出将以表格的形式进行,每行代表一个学生,每列代表一门科目的成绩,最后一列是平均分。

这个程序的设计理念是将每个功能模块化,使得每个部分都可以单独修改而不影响其他部分。这种设计方式易于维护和扩展,也使得代码逻辑更清晰。它主要包括数据收集模块、数据处理模块和数据展示模块,模块之间的交互通过共享的数据结构(在这个例子中是二维数组)来实现。



(二)集合类的使用

1、List 接口

List 接口继承了 Collection 接口,是一个允许存在重复项的有序集合;List接口常用的实现类有 ArryList、LinkList 等。
1.1 元素为基本类型的集合
(1)程序功能:录入任意多个学生的成绩,放入集合中(如90.5 88 87 67.5 78.5 70 92.5 75.5 80 82.5,……),计算学生成绩的平均分,并使用iterator遍历将所有成绩显示出来;
(2)利用 Collections 类的方法将成绩由高到低进行顺序和逆序排序,依次排序输出。


StudentScores.java代码

package com.xianyu.shuzu;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Collectors;

public class StudentScores {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        List<Double> scores = new ArrayList<>();

        System.out.println("输入学生成绩(输入非数字结束):");
        while (scanner.hasNextDouble()) {
            scores.add(scanner.nextDouble());
        }

        // 使用流来计算平均分
        double average = scores.stream()
                .mapToDouble(Double::doubleValue)
                .average()
                .orElse(0.0);
        System.out.println("平均分是:" + average);

        // 使用流来遍历所有成绩
        System.out.println("所有成绩:");
        scores.forEach(score -> System.out.print(score + " "));
        System.out.println();

        // 使用流来进行逆序排序(由高到低)
        List<Double> sortedScoresDesc = scores.stream()
                .sorted(Comparator.reverseOrder())
                .collect(Collectors.toList());
        System.out.println("成绩由高到低排序:");
        sortedScoresDesc.forEach(score -> System.out.print(score + " "));
        System.out.println();

        // 使用流来进行顺序排序(由低到高)
        List<Double> sortedScoresAsc = scores.stream()
                .sorted()
                .collect(Collectors.toList());
        System.out.println("成绩由低到高排序:");
        sortedScoresAsc.forEach(score -> System.out.print(score + " "));
        System.out.println();

        scanner.close();
    }
}




运行结果
java 数组与集合类 案例及解析



代码解析

  1. 读取学生成绩

    • 使用 Scanner 类从用户输入中读取成绩,直到用户输入非数字字符为止。这通过 hasNextDouble() 方法实现,该方法在用户输入非数字时返回 false,从而结束循环。
  2. 计算平均分

    • 使用Java 8的流(Stream)API,通过 stream() 方法将 List 转换为流,然后使用 mapToDouble(Double::doubleValue) 将每个元素映射为 double 类型,最后调用 average() 方法计算平均值。
  3. 遍历所有成绩

    • 使用 forEach 方法遍历列表中的每个元素,并打印出来。这种方法比传统的迭代器(Iterator)更简洁。
  4. 成绩排序

    • 使用流API进行排序,通过 sorted(Comparator.reverseOrder()) 实现从高到低的排序,而 sorted() 则实现从低到高的排序。排序后的结果通过 collect(Collectors.toList()) 重新收集到一个新的列表中。
  5. 输出排序后的成绩

    • 再次使用 forEach 方法遍历排序后的列表,并打印每个成绩。

整体来看,这段代码利用了Java 8引入的流(Stream)API,使得数据处理和转换更加简洁和直观。通过使用流,我们可以链式地调用多个操作,如映射、过滤、排序和收集,而不需要显式地使用迭代器或循环来处理集合中的元素。这种方法不仅提高了代码的可读性,也使得复杂的数据处理逻辑更加易于实现和管理。



1.2 元素为引用类型的集合

public class Student {
private String name;
private float score;
public Student() {
}
public Student(String name, float score) {
this.name = name;
this.score = score;
}
}

编写代码:
3(1)添加任意多个 Student 对象加入 List,并使用 iterator 遍历将成绩显示出来;
(2)利用 Collections 类的方法将成绩由高到低进行顺序和逆序排序,依次排序输出。


Student.class代码

package com.xianyu.shuzu;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
import java.util.Comparator;

public class Student {
    private String name;
    private float score;

    public Student(String name, float score) {
        this.name = name;
        this.score = score;
    }

    public float getScore() {
        return score;
    }

    public String getName() {
        return name;
    }

    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        Scanner scanner = new Scanner(System.in);

        System.out.println("请输入学生成绩,输入'q'结束:");
        while (scanner.hasNextFloat()) {
            float score = scanner.nextFloat();
            students.add(new Student("Student", score));
        }

        float sum = 0f;
        for (Student student : students) {
            sum += student.getScore();
        }

        // 计算平均分
        float average = sum / students.size();
        System.out.println("平均分是:" + String.format("%.1f", average));

        // 打印所有成绩
        System.out.println("所有成绩:");
        for (Student student : students) {
            System.out.print(student.getScore() + " ");
        }
        System.out.println();

        // 由高到低排序
        Collections.sort(students, Comparator.comparing(Student::getScore).reversed());
        System.out.println("成绩由高到低排序:");
        for (Student student : students) {
            System.out.print(student.getScore() + " ");
        }
        System.out.println();

        // 由低到高排序
        Collections.sort(students, Comparator.comparing(Student::getScore));
        System.out.println("成绩由低到高排序:");
        for (Student student : students) {
            System.out.print(student.getScore() + " ");
        }
        System.out.println();
    }
}




运行结果
java 数组与集合类 案例及解析



代码解析

  1. 读取学生成绩

    • 使用 Scanner 类从用户输入中读取成绩,直到用户输入非数字字符为止。这通过 hasNextFloat() 方法实现,该方法在用户输入非数字时返回 false,从而结束循环。
  2. 计算平均分

    • 通过遍历 List 中的所有 Student 对象,累加每个学生的成绩,然后除以学生总数来计算平均分。
  3. 打印所有成绩

    • 使用简单的增强型 for 循环遍历 List 中的每个 Student 对象,并打印其成绩。
  4. 成绩排序

    • 使用 Collections.sort() 方法结合 Comparator.comparing() 和方法引用 Student::getScore 来创建一个比较器,该比较器根据学生的成绩进行排序。
    • 使用 .reversed() 方法来反转比较器的顺序,实现从高到低的排序。
  5. 输出排序后的成绩

    • 在每次排序后,使用增强型 for 循环遍历排序后的 List,并打印每个学生的成绩。

整体来看,这段代码利用了Java 8引入的Lambda表达式和方法引用,使得排序逻辑更加简洁和直观。通过使用 Comparator.comparing() 和方法引用,我们可以直接根据 Student 对象的 score 属性进行排序,而不需要显式地创建匿名内部类来定义比较逻辑。这种方法不仅提高了代码的可读性,也使得复杂的数据处理逻辑更加易于实现和管理。



2、Set 接口

Set 接口也是继承 Collection 接口,不允许存储重复的元素;Set 接口常用的实现类HashSet、TreeSet 和 LinkedHashSet。
编写程序:
(1)将前面的代码,改用hashSet来实现,编写程序TestSet.java。并加入一个相同的Student对象;例如:
set.add(new Student("Jack",100));
set.add(new Student("Jack",100));
注意:运行程序时,可以发现同时打印出两个“Jack 100”;但这似乎和Set 不允许存储重复的元素相矛盾?
(2)重写完 Student 的类后,再运行代码,查看结果有何不同,分析原因;
原Student.java代码

package com.xianyu.shuzu;

public class Student {
    private String name;
    private float score;

    public Student(String name, float score) {
        this.name = name;
        this.score = score;
    }

    public float getScore() {
        return score;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return name + " " + score;
    }
}



TestSet.java代码

package com.xianyu.shuzu;

import java.util.HashSet;
import java.util.Set;

public class TestSet {
    public static void main(String[] args) {
        Set<Student> set = new HashSet<>();

        // 添加两个相同的Student对象
        set.add(new Student("Jack", 100));
        set.add(new Student("Jack", 100));

        // 打印结果
        for (Student student : set) {
            System.out.println(student);
        }
    }
}




运行结果
java 数组与集合类 案例及解析



重写Student.java代码

package com.xianyu.shuzu;

import java.util.Objects;

public class Student {
    private String name;
    private float score;

    public Student(String name, float score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public float getScore() {
        return score;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Student other = (Student) obj;
        return Float.compare(this.score, other.score) == 0 && Objects.equals(this.name, other.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, score);
    }

    @Override
    public String toString() {
        return name + " " + score;
    }
}




运行结果
java 数组与集合类 案例及解析



在Java中,Set 接口的实现类(如 HashSet)不允许存储重复的元素。然而,在默认情况下,Java中的对象比较是基于对象引用的,这意味着即使两个对象的内容完全相同,只要它们是通过不同的对象实例创建的,Set 也会认为它们是不同的。

在原始 Student 类中,没有重写 equals() 和 hashCode() 方法,因此 HashSet 使用默认的对象比较方式,即比较对象引用,而不是对象内容。这就是为什么在原始代码中,可以添加两个内容相同但引用不同的 Student 对象到 HashSet 中。

为了解决这个问题,需要重写 Student 类的 equals() 和 hashCode() 方法,以便 HashSet 可以根据对象的内容(即 name 和 score)来判断是否为重复元素。

在重写后的 Student 类中,equals() 方法比较了 name 和 score 属性,而 hashCode() 方法返回了一个基于 name 和 score 的哈希码。这样,当两个 Student 对象的 name 和 score 相同时,HashSet 将认为它们是相同的,从而不会重复添加。

运行重写后的 TestSet 类,你将看到只有一个 "Jack 100" 被打印出来,因为 HashSet 现在能够正确识别重复的 Student 对象。


3、Map 接口

Map 接口不是 Collecion 接口的继承;Map 接口定义存储“键(key)—值(value)”对,Map 中不能有重复的“键”,通过键来唯一标识的;Map 底层的“键”用Set 来存放,常用String 作为 Map 的键。
编写程序:
(1)请把学号和学生对象对应地放入 hashmap 中,以学号为键,学生对象为值,并通过 iterator 遍历将学生的学号、姓名和成绩显示出来。


Student.java代码

package com.xianyu.shuzu;

public class Student {
    private String name;
    private float score;

    public Student(String name, float score) {
        this.name = name;
        this.score = score;
    }

    // Getter and Setter 省略

    public String getName() {
        return name;
    }

    public float getScore() {
        return score;
    }

    @Override
    public String toString() {
        return "Student{name='" + name + '\'' + ", score=" + score + '}';
    }
}



TestMap.java代码

package com.xianyu.shuzu;

import java.util.HashMap;
import java.util.Map;

public class TestMap {
    public static void main(String[] args) {
        // 创建 HashMap 对象
        Map<String, Student> studentMap = new HashMap<>();

        // 添加一些学生对象
        studentMap.put("2022001", new Student("甲", 90));
        studentMap.put("2022002", new Student("乙", 85));
        studentMap.put("2022003", new Student("丙", 95));

        // 使用 for-each 遍历HashMap
        for (Map.Entry<String, Student> entry : studentMap.entrySet()) {
            String studentId = entry.getKey();
            Student student = entry.getValue();
            System.out.println("学号: " + studentId + ", 姓名: " + student.getName() + ", 成绩: " + student.getScore());
        }
    }
}




运行结果
java 数组与集合类 案例及解析



代码解析
这段代码主要包含了两个Java类:StudentTestMap。下面是对这两个类的简要解析:

Student.java

这个类定义了一个学生对象,包含两个私有属性:name(姓名)和 score(成绩)。类中定义了一个构造方法用于初始化这两个属性,以及提供了获取这些属性的方法(getter方法)。此外,还有一个重写的toString方法,用于返回学生对象的字符串表示,方便打印和调试。

TestMap.java

这个类展示了如何使用HashMap来存储和操作学生对象。主要步骤如下:

  1. 创建HashMap对象:使用Map<String, Student>类型创建了一个HashMap实例,其中键是学生的学号(String类型),值是对应的学生对象(Student类型)。

  2. 添加学生对象:通过调用put方法,将三个学生对象添加到HashMap中。每个学生对象通过学号关联。

  3. 遍历HashMap:使用增强的for循环遍历HashMapentrySet,这样可以同时访问键和值。对于每个条目,获取其键(学号)和值(学生对象),并打印出学号、姓名和成绩。

这段代码展示了Java中集合框架的使用,特别是HashMap的用法,以及如何存储和检索自定义对象。通过这种方式,可以方便地根据学号查找学生信息,这在实际的学校管理系统中非常有用。