Say we have a class of 25 students, and the TDSB has assigned us to make a program that will calculate the average for the entire class. From our current knowledge of Java, we would need to declare 25 different variables and have lines upon lines of code get the mark from each student and add it to the sum.
Student s1 = new Student (“Bob”);
Student s2 = new Student (“Alice”);
…
Student s25 = new Student (“Kevin”);
int sum = 0;
sum += s1.getMark();
sum += s2.getMark();
…
sum += s25.getMark();
This incredibly inefficient method of storing 25 students would become a disaster and a mess to organize or reuse. Instead, we use the data structures of Arrays and ArrayLists.
To begin our discussion of Arrays, let’s use a visual diagram to help represent an array. Let's say that this array is an array of integers with length 8. In code, we declare this by using:
int [] array = new int [8];
The first word specifies the data type that this array will be holding. The pair of square brackets signifies that this variable is an array. The number of pairs of square brackets will signify the dimension of the array. The next word then specifies the name of the array, which in this case, is simply “array”. After the equals sign, we see the keyword new. This keyword is also used in OOP, where new instantiates a new object. However, instead of a constructor following the “new” keyword, we get the data-type once again, and the size of the array in that dimension between two square brackets.
There is also a method of declaring an array with specific values. This will be the array we will be using.
int [] array = {1,2,3,4,5,6,7,8};
We can now see the initial state of our array in a visual representation.
Arrays simply store each object or value as a position, otherwise known as an index. In this case, we can see that each box has a label from 0 to 7. Note that the index begins from 0. This is known as being zero-indexed, and is the standard for most programming languages. We can also see that each box already has a default value for the primitive data type. In this case, it will be 0. If the data-type was not primitive, all boxes would instead be instantiated to null. Now, say we want to store an integer, say 5, at a specific index, say 2. In code, it would be:
array[2] = 5;
Similar to how we set a value to a variable, we use the variable, the equals sign, and then the value. However, in this case, we can access the variable at a certain index using this notation: nameOfArray[index]
. This is what happens to the array after the code has run:
Now say we want to retrieve a value from this array at index 3. Similar to the notation discussed earlier, we can access the variable at a certain index by using nameOfArray[index]. In this case, instead of setting it to some value, we will print it out:
System.out.println(array[3]);
This will print out 0, as if we refer back to our previous diagram for our array, we can see that the value at index 3 is 0. Now say we want to traverse the entire array, summing up the total of all array values. For this, we can either use a for-loop or a for-each loop.
Using for loops to traverse an array is relatively simple. Assuming that you, the reader, has a basic understanding of for loops, we just need to make a for loop that will traverse from 0, to 7. Note that 7 is the length of the array-1. The end point will always be this value, so we do not need to always change the end point whenever we traverse a different array with a different size. The basic code for traversing an array is:
for (int i = 0; i < array.length; i++){
array[i]; //use value of variable to process or set a value for this variable
}
As you can see, variable i will begin at 0 and continuing incrementing by 1 until it reaches array.length-1. After this point, it will increment to array.length, which means it will no longer satisfy the for-loop conditional. Back to our problem, the solution would be:
int sum = 0;
for (int i = 0; i < array.length; i++){
sum += array[i];
}
The sum variable will then hold the value of the sum.
Using a for-each loop to traverse an array is even simpler than a for loop. However, you lose some functionality in the process. A for-each loop can retrieve the value at every index in the array, but it can not set values and does not keep track of the current index. Its basic structure is:
for (datatype variableName : nameOfArray){
variableName; //This will contain the value at a certain index of the array nameOfArray. It can not be directly changed with an = sign.
}
Solving our previous problem using for-each loops looks like:
int sum = 0;
for (int i : array){
sum+= i;
}
Now, we are finally able to solve the problem of computing the average grade in a class of 25 students. Let's assume that the array of students has already been initialized with all the correct data, and is named arrayOfStudents. In this case, we will have both a solution using a for-each loop and a solution using a for loop.
int sum = 0;
for (int i = 0; i < arrayOfStudents.length; i++){
sum += arrayOfStudents[i];
}
double average = (double)sum/arrayOfStudents.length;
int sum = 0;
for (int i : arrayOfStudents){
sum += i;
}
double average = (double)sum/arrayOfStudents.length;
Now say we have a more difficult problem. You have a file with an unknown number of student grades, and you want to find the top 10 of them. The FileIO part of this problem is unnecessary to what we are discussing right now, and so we will simply say that there is a magic method that will read the next integer in a file.
public static int nextInt(){
//implementation not shown, returns next integer, or if end of file reached, -1
}
Sorting is also discussed in a different page, so we will have a magic method that will sort for us.
public static ArrayList sort(ArrayList list){
//implementation not shown, returns sorted list in descending order
}
Using arrays would be difficult and inefficient, as you would have to loop through the file once to find the length first, then use an array of that length to store all the values. Instead, we will solve the problem using the ArrayList data structure. Let’s first discuss what the ArrayList is.
The ArrayList data structure is generally a part of the data structure group known as a Dynamic Array. It can change its size continuously so that values can be continuously added into the ArrayList. This is similar a list in real life, where there is no fixed size and items can be added easily. Let’s start by creating an ArrayList of Integers.
ArrayList arrayList = new ArrayList();
Notice that the ArrayList is not of type int, but instead of type Integer. This is because ArrayLists can not function with primitive data types. The Integer class is known as a wrapper class, as it simply serves as a class representation of int. Currently, the ArrayList has no items and has a size of 0. We can add items using the method .add(Integer value).
arrayList.add(0);
arrayList.add(2);
We can also add using the overload .add(int index, Integer value).
arrayList.add(1, 5);
Now we have an arrayList with three items, 0, 5, and 2.
Let’s say we want to remove the item at index 0. To do this, we use the method .remove(int index).
arrayList.remove(0);
The new array looks like:
Let’s say we want to remove the first instance of the number 2. To do this, we use the method .remove(Integer value).
arrayList.remove((Integer)2);
The 2 is casted to the wrapper class to avoid confusion between this overload of remove and the previous overload. The array now looks like:
Now say we want to change the value at index 0 into a 3. For this, we use the method .set(int index, Integer newValue).
arrayList.set(0, 3);
The array now looks like:
Now we discuss the traversal of an ArrayList. Similar to the traversal of an Array, an ArrayList may also use both a for loop and a for-each loop. The for-each loop is simpler but still has the same restriction; the current object of the for-each loop can not be modified directly using the = sign. However, mutators and accessors can be used to change the value of the object indirectly.
The method to find the size of the array is .size(). We must use this because the ArrayList does not have a fixed size and therefore the for loop cannot loop through a constant length.
for (int i = 0; i < arrayList.size(); i++){
arrayList.get(i); //use the Integer value for processing
arrayList.set(i, newValue); //set the value at the index
}
The for each loop can not set the object directly, instead having to use mutators. Instead of having an ArrayList of Integers, let’s say we have a class known as Students, with a simple instance variable of grade, and a mutator and accessor for that value.
public class Student{
private int grade;
public Student (int g){
grade = g;
}
public void setGrade(int g){
grade = g;
}
public int getGrade(){
return grade;
}
}
Let’s have an ArrayList of them known as listOfStudents. Let’s assume that we have already initialized them with Students. Here's the code for traversing that ArrayList.
for (Student s: listOfStudents){
s.getGrade(); //The grade can be used for processing.
s.setGrade(value); //The grade of the student that is being traversed at that point can be modified using setGrade.
}
As you can see, Student s can be modified indirectly with .setGrade(). Now that we know how to use an ArrayList, we can solve the initial problem of the average of the top 10 scores from a file with unknown size.
int currentScore = nextInt();
int sum = 0;
ArrayList scores = new ArrayList();
while(currentScore != -1){
scores.add(currentScore);
currentScore = nextInt();
}
scores = sort(scores);
for (int i = 0; i < 10; i++){ //top 10
sum += scores.get(i);
}
double average = (double)sum/10;