Introduction to Objects
2.15 How to Return More Than One Value: Voting
Strive not to be a success, but rather to be of value.
Whatever the mind can conceive and believe, it can achieve.
There is only one way to avoid criticism: do nothing, say nothing, and be nothing.
When run, Program P2.4 stores its output in the file output.txt. Here is the output:
Words Frequency a 1 achieve 1 and 2 avoid 1 be 3 believe 1 but 1 can 2 conceive 1 criticism 1 do 1 is 1 it 1 mind 1 not 1 nothing 3 of 1 one 1 only 1 rather 1 say 1 strive 1 success 1 the 1 there 1 to 3 value 1 way 1 whatever 1
A file,
• votes.txt, contains the names of the candidates. The first name is considered as candidate 1, the second as candidate 2, and so on. The names are followed by the votes. Write a program to read the data and evaluate the results of the election. Print all output to the file results.txt.
Your output should specify the total number of votes, the number of valid votes, and the
•
number of spoiled votes. This is followed by the votes obtained by each candidate and the winner(s) of the election.
Given this data in votes.txt:
Nirvan Singh Denise Duncan Avasa Tawari Torrique Granger Saskia Kalicharan Dawren Greenidge Jordon Cato
3 1 6 5 4 3 5 3 5 3 2 8 1 6 7 7 3 5 6 9 3 4 7 1 2 4 5 5 1 4 0
your program should send the following output to results.txt:
Invalid vote: 8 Invalid vote: 9 Number of voters: 30 Number of valid votes: 28 Number of spoilt votes: 2 Candidate Score Nirvan Singh 4 Denise Duncan 2 Avasa Tawari 6 Torrique Granger 4 Saskia Kalicharan 6 Dawren Greenidge 3 Jordon Cato 3 The winner(s)
Avasa Tawari Saskia Kalicharan
We will use the following outline for solving this problem:
get the names and set the scores to 0 process the votes
print the results
We need to store the names of the seven candidates and the score obtained by each. We could use a String array for the names and an int array for the scores. But what if we needed to store many more attributes of a candidate? For each attribute, we would need to add another array of the appropriate type. To cater for this possibility and to make our program more flexible, we will create a class Person and use an array of Person.
What will this Person class look like? For our problem, it will have two instance fields, name and numVotes, say.
We define it as follows:
class Person { String name;
int numVotes;
Person(String s, int n) { name = s;
numVotes = n;
}
} //end class Person
To cater for seven candidates, we set the symbolic constant MaxCandidates to 7 and declare the Person array candidate as follows:
Person[] candidate = new Person[MaxCandidates+1];
We will use candidate[h] to store information for candidate h, h = 1, 7; we will not use candidate[0]. This will enable us to process the votes more naturally than if we had used candidate[0]. For example, if there is a vote for candidate 4, we want to increment the vote count for candidate[4]. If we had used candidate[0] to store information for the first candidate, we would have had to increment the count for candidate[3], given a vote of 4. This can be misleading and disconcerting.
Suppose in is declared as follows:
Scanner in = new Scanner(new FileReader("votes.txt"));
We will read the names and set the scores to 0 with the following code:
for (int h = 1; h <= MaxCandidates; h++) candidate[h] = new Person(in.nextLine(), 0);
When this code is executed, we can picture candidate as shown in Figure 2-11. Remember, we are not using candidate[0].
Next, we must process the votes. We will delegate this to the function processVotes. This will read each vote and add 1 to the score for the appropriate candidate. Thus, if the vote is 5, it must add 1 to the score for candidate 5.
Another task of this function is to count the number of valid and spoiled votes and return these values to main.
But how does a function return more than one value? Well, it can return one value—an object—and this object can contain many fields.
In this example, we can declare a class (VoteCount, say) with two fields and a constructor, like this:
class VoteCount { int valid, spoilt;
VoteCount(int v, int s) { valid = v;
spoilt = s;
} }
The following statement will create an object called votes and set votes.valid and votes.spoilt to 0.
VoteCount votes = new VoteCount(0, 0);
We could also have dispensed with the constructor and created the object with this statement:
VoteCount votes = new VoteCount();
name: Nirvan Singh numVotes: 0
name: Denise Duncan numVotes: 0
name: Joshua Regrello numVotes: 0
name: Torrique Granger numVotes: 0
name:Saskia Kalicharan numVotes: 0
candidate[1]
candidate[2]
candidate[3]
candidate[4]
candidate[5]
candidate[6]
candidate[7]
name: Dawren Greenidge numVotes: 0
name: Jordon Cato numVotes: 0
Figure 2-11. Array candidate after reading the names and setting the scores to 0
This would initialize the fields to 0, but it is better to do so explicitly, like this:
votes.valid = votes.spoilt = 0;
When we read a valid vote, ++votes.valid adds 1 to votes.valid, and when we read an invalid vote, ++votes.
spoilt adds 1 to votes.spoilt. At the end, the function will return votes— an object containing the two counts.
Finally, we must write printResults, which prints the results in the format specified earlier. First we print the total number of votes broken down into valid and spoiled votes. Then, using a for loop, we print the individual scores.
Next, it determines the winning score by calling getLargest to find the candidate whose numVotes field is the largest. This is accomplished by these statements:
int win = getLargest(list, 1, MaxCandidates);
int winningVote = list[win].numVotes;
Here, list is the Person array. Using winningVote, it then makes another pass through the array looking for candidates with this score. This ensures that if there are ties for the winner, all will be printed. Program P2.5 is the complete solution for solving this voting problem.
Program P2.5
import java.util.*;
import java.io.*;
public class Voting {
final static int MaxCandidates = 7;
public static void main(String[] args) throws IOException { Scanner in = new Scanner(new FileReader("votes.txt"));
PrintWriter out = new PrintWriter(new FileWriter("results.txt"));
Person[] candidate = new Person[MaxCandidates+1];
//get the names and set the scores to 0 for (int h = 1; h <= MaxCandidates; h++) candidate[h] = new Person(in.nextLine(), 0);
VoteCount count = processVotes(candidate, MaxCandidates, in, out);
printResults(out, candidate, MaxCandidates, count);
in.close();
out.close();
} //end main
public static VoteCount processVotes(Person[] list, int max, Scanner in, PrintWriter out) { VoteCount votes = new VoteCount(0, 0); //set valid, spoilt counts to 0
int v = in.nextInt();
while (v != 0) {
if (v < 1 || v > max) {
out.printf("Invalid vote: %d\n", v);
++votes.spoilt;
} else {
++list[v].numVotes;
++votes.valid;
}
v = in.nextInt();
} //end while return votes;
} //end processVotes
public static void printResults(PrintWriter out, Person[] list, int max, VoteCount votes) { out.printf("\nNumber of voters: %d\n", votes.valid + votes.spoilt);
out.printf("Number of valid votes: %d\n", votes.valid);
out.printf("Number of spoilt votes: %d\n", votes.spoilt);
out.printf("\nCandidate Score\n\n");
for (int h = 1; h <= MaxCandidates; h++)
out.printf("%-18s %3d\n", list[h].name, list[h].numVotes);
out.printf("\nThe winner(s)\n");
int win = getLargest(list, 1, MaxCandidates);
int winningVote = list[win].numVotes;
for (int h = 1; h <= MaxCandidates; h++)
if (list[h].numVotes == winningVote) out.printf("%s\n", list[h].name);
} //end printResults
public static int getLargest(Person[] list, int lo, int hi) { int big = lo;
for (int h = lo + 1; h <= hi; h++)
if (list[h].numVotes > list[big].numVotes) big = h;
return big;
} //end getLargest } //end class Voting class Person { String name;
int numVotes;
Person(String s, int n) { name = s;
numVotes = n;
}
} //end class Person class VoteCount { int valid, spoilt;
VoteCount(int v, int s) { valid = v;
spoilt = s;
}
} //end class VoteCount
If we wanted to print the results in alphabetical order, we could do so by calling selectionSort (Section 2.13) with this statement:
selectionSort(candidate, 1, MaxCandidates);
We could achieve the same result by calling insertionSort (Section 2.13) like this:
insertionSort(candidate, 1, MaxCandidates);
But suppose we wanted to print the names of the candidates in descending order by number of votes received, that is, with the winning candidate(s) first. To do this, the object array candidate must be sorted in descending order using the numVotes field to control the sorting. This could be done by the following call where sortByVote uses an insertion sort (any sort will do) and is written using the formal parameter list:
sortByVote(candidate, 1, MaxCandidates);
We can write sortByVote as follows:
public static void sortByVote(Person[] list, int lo, int hi) { //sort list[lo] to list[hi] in descending order by numVotes for (int h = lo + 1; h <= hi; h++) {
Person hold = list[h];
int k = h - 1; //start comparing with previous item while (k >= lo && hold.numVotes > list[k].numVotes) { list[k + 1] = list[k];
--k;
}
list[k + 1] = hold;
} //end for } //end sortByVote
Suppose we add sortByVote to Program P2.5, and we insert this statement:
sortByVote(candidate, 1, MaxCandidates);
just before this one:
printResults(out, candidate, MaxCandidates, count);
If we run the program with the same data as before, it will produce the following output. The candidates are printed with the highest score first and the lowest score last.
Invalid vote: 8 Invalid vote: 9 Number of voters: 30 Number of valid votes: 28 Number of spoilt votes: 2 Candidate Score Avasa Tawari 6 Saskia Kalicharan 6 Nirvan Singh 4 Torrique Granger 4 Dawren Greenidge 3 Jordon Cato 3 Denise Duncan 2 The winner(s)
Avasa Tawari Saskia Kalicharan