Skip to main content
Logo image

Java, Java, Java: Object-Oriented Problem Solving, 2022E

Section 6.7 Example: Computing Averages

Suppose you want to compute the average of your exam grades in a course. Grades, represented as real numbers, will be input from the keyboard using our KeyboardReader class. To signify the end of the list, we will use a sentinel value—9999 or \(-1\) or some other value that won’t be confused with a legitimate grade. Because we do not know exactly how many grades will be entered, we will use a noncounting loop in this algorithm. Because there could be no grades to average, we will use a while structure so it is possible to skip the loop entirely in the case that there are no grades to average.
The algorithm should add each grade to a running total, keeping track of the number of grades entered. Thus, this algorithm requires two variables: one to keep track of the running total and the other to keep track of the count. Both should be initialized to 0. After the last grade has been entered, the total should be divided by the count to give the average. In pseudocode, the algorithm for this problem is as follows:
initialize runningTotal to 0         // Initialize
initialize count to 0
prompt and read the first grade      // Priming read
while the grade entered is not 9999 {// Sentinel test
    add it to the runningTotal
    add 1 to the count
    prompt and read the next grade   // Update}
if (count > 0)           // Guard against divide by 0
    divide runningTotal by count
output the average as the result
Note that in this problem our loop variable, grade, is read before the loop test is made. This is known as a priming read. It is necessary in this case, because the loop test depends on the value that is read. Within the loop’s body, the updater reads the next value for grade. This is a standard convention for coding while structures that involve input, as this problem does. Note also that we must make sure that count is not 0 before we attempt to compute the average because dividing by 0 would cause a divide-by-zero error.
Translating the pseudocode algorithm into Java raises several issues. Suppose we store each grade that is input in a double variable named grade. The loop will terminate when grade equals 9999, so its entry condition will be (grade != 9999). Because this condition uses grade, it is crucial that the grade variable be initialized before the bound test is made. This requires a priming read. By reading the first value of grade before the loop entry condition is tested, ensures that the loop will be skipped if the user happens to enter the sentinel (9999) on the very first prompt. In addition to reading the first exam score, we must initialize the variables used for the running total and the counter. Thus, for our initialization step, we get the following code:
double runningTotal = 0;
int count = 0;
reader.prompt("Input a grade (e.g., 85.3) " +
      "or 9999 to indicate the end of the list >> ");
double grade =
     reader.getKeyboardDouble(); // Priming input
Within the body of the loop we must add the grade to the running total and increment the counter. Since these variables are not tested in the loop entry condition, they will not affect the loop control. Our loop updater in this case must read the next grade. Placing the updater statement at the end of the loop body will ensure that the loop terminates immediately after the user enters the sentinel value:
while (grade != 9999) {    // Loop test: sentinel
  runningTotal += grade;
  count++;
  reader.prompt("Input a grade (e.g., 85.3) " +
      "or 9999 to indicate the end of the list >> ");
  grade = reader.getKeyboardDouble(); // Update:input
} // while
You can see that it is somewhat redundant to repeat the same statements needed to do the initializating and the updating of the grade variable. A better design would be to encapsulate these into a method and then call the method both before and within the loop. The method should take care of prompting the user, reading the input, converting it to double, and returning the input value. The method doesn’t require a parameter:
private double promptAndRead() {
  reader.prompt("Input a grade (e.g., 85.3) " +
     "or 9999 to indicate the end of the list >> ");
  double grade = reader.getKeyboardDouble();
                          // Confirm input
  System.out.println("You input " + grade + "\n");
  return grade;
}
Note that we have declared this as a private method. It will be used to help us perform our task but won’t be available to other objects. Such private methods are frequently called helper methods.
This is a much more modular design. In addition to cutting down on redundancy in our code, it makes the program easier to maintain. For example, there is only one statement to change if we decide to change the prompt message. It also makes the program easier to debug. Input errors are now localized to the promptAndRead() method.
Another advantage of encapsulating the input task in a separate method is that it simplifies the task of calculating the average. This task should also be organized into a separate method:
public double inputAndAverageGrades() {
  double runningTotal = 0;
  int count = 0;
  double grade = promptAndRead();// Priming initializer
  while (grade != 9999) {      // Loop test: sentinel
    runningTotal += grade;
    count++;
    grade = promptAndRead();// Update: get next grade
  } // while
  if (count > 0)      // Guard against divide-by-zero
    return runningTotal / count;// Return the average
  else
    return 0;         // Special (error) return value
  }
Note that we have declared this as a public method. This will be the method you call to calculate your course average.
Because we have decomposed the problem into its subtasks, each subtask is short and simple, making it easier to read and understand. As we saw in the checkerboard example, the use of small, clearly-focused methods is a desireable aspect of designing a program.

Activity 6.7.1. Average.

The complete Average.java application is shown in Listing 6.7.3 or at Average on repl.
import java.io.*;
public class Average {                            // Console I/O
  private KeyboardReader reader = new KeyboardReader();
  private double promptAndRead() {
    reader.prompt("Input a grade (e.g., 85.3) " +
           "or 9999 to indicate the end of the list >> ");
    double grade = reader.getKeyboardDouble();
    System.out.println("You input " + grade + "\n");// Confirm input
    return grade;
  }
  public double inputAndAverageGrades() {
    double runningTotal = 0;
    int count = 0;
    double grade = promptAndRead();  // Initialize: priming input
    while (grade != 9999) {          // Loop test: sentinel
      runningTotal += grade;
      count++;
      grade = promptAndRead();       // Update: get next grade
    } // while
    if (count > 0)         // Guard against divide-by-zero
      return runningTotal / count;   // Return the average
    else
      return 0;            // Special (error) return value
 }
 public static void main(String argv[]) {
   System.out.println("This program calculates average grade.");
   Average avg = new Average();
   double average = avg.inputAndAverageGrades();
   if (average == 0)                             // Error check
      System.out.println("You didn't enter any grades.");
   else
      System.out.println("Your average is " + average);
 } // main()
} // Average
Listing 6.7.3. A program to compute average grade using a while structure.
Try it below.
One final point about this program is to note the care taken in the design of the user interface to explain the program to the user, to prompt the user before a value is input, and to confirm the user’s input after the program has read it.
You have attempted of activities on this page.