Using Next... methods
When do we need to use the hasNext...() methods, and when should we not use them?
One thing I've noticed is that a lot of you have been using a triple nested loop with the outer one being
while (scan.hasNext...()) {
...
}
And then the inner ones are looping through rows and cols.
Pondy has used
hasNext()
quite often when dealing with files in the lecture examples, so it seems like we should use it here too? Actually no.
You actually shouldn't be using
hasNext...()
at all for the core.
hasNext...()
basically says whether or not there is a next (of whatever type, double, int, or just string) in the file. This is useful if you don't know how much more data is left in the file. But in the case of where you do, it is unnecessary.
Before we try and understand when we DON'T need it, lets start by looking at an example where we SHOULD use it.
If you have a whole bunch of doubles in a file that represent temperatures (that were for example collected from a weather station), but you don't know how many are in the file, you would need to use
hasNextDouble()
to keep reading them while there was some left. For example, lets say we had had this file of temperatures, we need to find the average and we have made a Scanner called scan that is reading the file. We'd do something like...
double sum = 0;
double count = 0;
while (scan.hasNextDouble()) { //While there is still another temperature in the file
double nextTemp = scan.nextDouble(); // Get the next temperature from the file and put it in this variable
count = count + 1; // Increment the count by 1
sum = sum + nextTemp; //Add this temperature to the sum we are keeping track of
UI.println("The average is " + (sum/count)); // Print the average
The
hasNextDouble()
is essential to ensure that we don't continue to try and read temperatures when there are none left. If we just tried to read them forever, i.e. had a loop condition of
while (true)
, when there was none left it would crash the program! We don't know how many there are, so we can't simply say something like
while (count < 100)
, as we don't know for sure if there will be 100. There might be 50, there might be 10, there might be 10,000. hasNextDouble() allows us to loop while there are still doubles (i.e. temperatures) left.
So, we can see why we needed to use the
hasNextDouble()
method above. It is the same idea for
hasNext()
(interpret the next thing as a string; even if it was an integer, it could read that as a string, i.e. "3462" or "3833.594"), and for
hasNextInt()
. They are useful if we don't know how much is left in the file.
However, in some cases we DO know how much data is left, or we are allowed to assume we know that the data is in the format we were told it would be. This is like the image renderer. So, we know that the file should start with the magic token, followed by the number of rows, then the number of columns, then the depth, and then the data saying what colour each pixel should be. If it wasn't in that format (i.e. there was something wrong with the file), then that isn't your program's problem; your program would crash for it and that'd be the fault of the person who made the image file).
So anyway, we can safely read those first 4 pieces of information. So now, how do we know how many integers follow? Remember that we know how many rows are in the image, and how many columns. There will be rows x columns pixels in the image (e.g. if rows is 10, and columns is 30, there will be 300 pixels). Each pixel is specified with 3 integers (red, green, blue amounts). So, in total we will need...
rows x cols x 3
integers in the file, in order to draw the image.
And we can safely assume that this is how many there are! If you have a loop that does something like
while (row < rows) {
while (col < cols) {
... read the colours for this pixel, and then draw the pixel
}
}
If this example is confusing you, you may need to go back to the loops lecture slides, and have a look at the grid of circles that pondy showed you. Remember that the outer loop says which row we are drawing, and the inner loop says which col we are currently drawing in that row (and a row only has columns of length 1). i.e. a row is "ooooooooo" (assuming we are drawing circles),and a single column in that row is "o"
And by drawing the number or rows specified, we can draw lots of rows
ooooooooo
ooooooooo
ooooooooo
ooooooooo
ooooooooo
The number of circles is the number of rows multiplied by the number of columns. The number of times the code in the
inner loop of 2 nested loops will run will always be the number of times the outer loop iterates multiplied by the number of times the inner loop iterates
But yeah, this isn't the point of this post, its a bit of a sidetrack, if you are confused, check the lecture notes, as for help on the forum, go to the tutorial, or ask a tutor in the lab. It is ESSENTIAL that you understand this concept of how a nested loop of rows and columns works. It comes up again in later assignments! And is essential for understanding nested loops in general.
Anyway, this loop will ensure you are reading
rows x cols x 3
integers (assuming that the bit where it is reading the colours is reading 3 values; one for each of red, green, and blue).
And in the case of the core, this is all the data in the file. See how we knew what was there, without ever needing to use a
hasNext...()
method to check? If there happened to be less data in the file, the program would crash, but that isn't its fault, it is the fault of the person who gave it an incorrectly formatted file. The
P3
token at the start essentially says "What follows is a correctly formatted P3 file". If there is extra data in the file, you don't care about it. So there is no reason to use
hasNext...()
.
The general rule is: If you know how much of something, (and what it is) is in the file, you don't need to use
hasNext...()
methods. If there is something you don't know how much there is, then you will need to use
hasNext...()
methods to read the data until there is none of it left.
Yes, your
ImageRenderer
core might work correctly even with the extra loop. This is because it will only ever iterate once through that outer
hasNext...()
loop, as the first time it will see there is data left, and then when it gets back to it a second time, all the data has been read and it just goes false and doesn't loop again. So it works, but it shows a lack of understanding on your part, and may lose you marks if it is there!!!
In the case of the completion, there IS something you don't know; you don't know how many frames there are in the animation. For this particular part, you may need to use the
hasNext...()
methods in order to keep reading frames while there are frames remaining. HOWEVER, the reading of a frame itself won't need the
hasNext...()
methods; as you know what is in the core. So essentially the
hasNext...()
loop would be serving the purpose of checking whether or not there are still
frames remaining in the file.
I will leave it up to you to figure out how to handle the comments that are in the completion animations. Remember... what things do you know how many there will be of? What things do you not know how many there will be of?
The
ExamTimes
program is an example of where we don't know how many lines there will be, but we know what is actually on each line as it is the same every time. So you'd need to use
hasNextLine()
(or
hasNext()
can work, depending on how you approached it) in order to keep going while there are lines left (as we don't know how many there are), but then you can assume that when reading the data
within a line, the line is in the format specified. This is a very common kind of file format you might need to read.