NetBeans Project

Quiz data for a group of students

Last week I gave a lecture on the Java Set and Map classes. Those classes offer us a way to store and retrieve information quickly and efficiently. Today's example program will demonstrate the use of these classes to organize information. Specifically, we will construct a program to record and manage data for a group of students in a class.

The starting point for the problem is a file containing quiz scores for a group of students. Entries in this file take the form

<student name> <quiz number> <quiz score>

Here is what some typical rows in this file will look like:

D 1 61
C 4 77
L 4 99
I 2 94
K 7 95
B 3 66

Because students work at their own pace and quiz scores get recorded as students submit their work, the quiz scores come in in an unpredicatable order.

The program we will construct will print two reports based on the data in the text file.

Setting up the classes

To build our program we are going to need to construct two classes: a Student class that records quiz grades for individual students, and a Roster class that stores all of the Student objects we are going to create.

The Student class will have this structure:

public class Student {
    private String name;
    private TreeMap<Integer,Integer> grades;

    public Student(String name) {
        this.name = name;
        grades = new TreeMap<Integer,Integer>();
    }

    public String getName() { return name; }

    public void recordGrade(int quiz,int grade) {
        grades.put(quiz, grade);
    }
}

Because quizzes have both a quiz number and an associated score, I am going to be storing the quiz grades in a TreeMap that uses the quiz number as its key and the associated score as its value.

The Roster class will have this structure:

public class Roster {
    private TreeMap<String,Student> roster;
    private TreeSet<Integer> quizzes;

    public Roster() {
        roster = new TreeMap<String,Student>();
        quizzes = new TreeSet<Integer>();
    }
}

The member variable roster stores the student objects. Since we will need to look up students by their names, the roster is a map that uses the student's name as the key and the Student object as the value.

I have also added a second member variable, quizzes, that I will use to keep track of which quizzes have had at least one student submit that quiz. This will be useful later for generating the report of which quizzes the students have not submitted yet.

Reading the data from the file

Here is the code for a readScores() method we will add to the Roster class.

public void readScores(String fileName) {
    Scanner input = null;
    try {
        input = new Scanner(new File(fileName));
    } catch(Exception ex) {
        System.out.println("Could not open file.");
        System.exit(1);
    }

    while(input.hasNext()) {
        String name = input.next();
        int quiz = input.nextInt();
        int grade = input.nextInt();

        if(!roster.containsKey(name))
            roster.put(name, new Student(name));

        Student st = roster.get(name);
        st.recordGrade(quiz, grade);

        if(!quizzes.contains(quiz))
            quizzes.add(quiz);
    }
    input.close();
}

The while loop will read the individual rows in the text file. Each row will start with a student's name: the first thing we need to check is whether or not we have created a Student object for this student. If not, we go ahead and make a new Student object for this student and put that object in the roster. We then look up the student in the roster and tell that student object to record a grade for that quiz.

Each time we read an entry that mentions a particular quiz number, we will also check to see if we have a number in our quizzes set for that quiz. If not, we will add that quiz number to the set of quizzes we have seen.

Printing the reports

Once we have loaded all of our data into the program we will be ready to print the two reports. The first step in printing these reports is to add methods to the Student class that will allow a Student to compute a current quiz average and print a list of quizzes they have not yet taken.

public double quizAverage() {
    double sum = 0;
    for(int score : grades.values())
        sum += score;
    return sum/grades.size();
}

public void printMissing(TreeSet<Integer> quizzes,PrintWriter out) {
    for(int n : quizzes) {
        if(!grades.containsKey(n))
            out.println(name + " " + n);
    }
}

With those two methods added to the Student class we can add methods to the Roster class to generate the two reports we need.

public void printMissingQuizzes() {
    PrintWriter out = new PrintWriter(System.out);
    for(Student st : roster.values())
        st.printMissing(quizzes, out);
    out.close();
}

public void printAverages() {
    for(Student st : roster.values()) {
        System.out.print(st.getName() + ":");
        System.out.printf("%.1f\n",st.quizAverage());
    }
}