Learn Programming in Java

<<Previous | ToC | Next >>

Lesson #5: Arrays

An important use of iteration is to step through arrays of data, one item at a time. An array is any data type, repeated (different values) some specified number of times. Except for "final" arrays of constants, Java arrays are all dynamic, which is sort of like an object, only different. We will get into objects later, but objects and arrays are both created using the keyword "new":
int[] intary; // declared, but not yet exists
int[] myary = new int[99]; // declared and created but (probably) undefined
final int[] nums = {3,1,4,1,5,9,2,6,5,3,5}; // constant array with 11 predefined elements
intary = new int[1000]; // now allocated memory for 1000 numbers
for (int n=0; n<1000; n++) intary[n] = 0; // now it has known values
for (int i=0; i<99; i++) myary[i] = nums[i%11]; // filled with 9 copies of nums

Underneath the covers a String is also an array, but (as we have already seen) it has special uses, and mostly we do not want to step through the characters one at a time. When we do, there is an accessor method to do that, but it only lets us look; for efficiency, you are not allowed to change the characters in a String, you can only make a new String as changed. But you can make arrays of String, and even arrays of arrays.

Like C (which Java mostly copied), all Java arrays start at element number ("index") zero, and the last element in any array has an index one less that the number you used to create it. It is an error if you try to access the array with an index less than zero or greater than the highest actual element index. Better languages than C do not allow that error, but one of the problems with C is that the compiler/runtime has no way to know if you are staying within the bounds of your array. The result is a whole bunch of "security errors" that software vendors are constantly patching.

Java checks every array access: the array must exist, and the specified index must be within the bounds. This costs something at runtime, but not much more than a well-written C program that does its own checking. It's lots faster than crashing or (as in C) destroyuing other data or stealing secrets. My compiler notices if you did your own checking, and omits its own if so, but I don't think Java does that. So if you look at my example code, or especially the runtime code for my Game Engine, you will see all these checks for null (array not yet allocated) and index bounds. It's a good habit, and as compilers get smarter, they will remove their own (now superfluous) checks and your code will run faster and safer. Right now, Java is just safer (which is not a bad thing). In the examples above, null checks are not needed, because the allocation obviously preceeds the access. Range checks are not needed because the for-loop keeps the index within bounds, except for the access to "nums[i%11]" where the modulus operator ensures the computed index cannot exceed the array bounds.

Most programming languages require you to specify all the dimensions when you declare a multi-dimensional array. Two-dimensional Java arrays are defined to be arrays of arrays, and Java arrays are given a dimension (the number of elements) when memory is allocated, which could happen several times in the course of a program, and even for different sizes, so there's no requirement for the second dimention to be uniform, nor even for all the elements to exist. This imposes a small performance penalty, about the same as accessing the first dimension of an array, but larger than in languages where multi-dimensionality is fixed at declaration time. This is not a problem, but you need to be aware of the differences when you move to another programming language. Me, I'm always pushing at the performance limits of the computer, so I look for opportunities to make my program run faster.

Later on we will be programming a Tic-Tac-Toe game. We can define the game board as TTTboard[3][3], or we can linearize it to TTTboard[9] and separate the rows and columns in software, which is somewhat faster -- but not enough to notice. Programmers get to make these kinds of choices and trade-offs. But Tic-Tac-Toe is pretty challenging. Lets start with an easier game.


Let's write a game program that uses iteration and arrays and other fun stuff. You probably played Hangman when you were younger. We will write a program that plays scorekeeper for two humans playing hangman. Maybe later on you can improve it to play one side, but that requires access to a dictionary and stuff I don't want to get into at this time.

Vivian Killian provided the basic structure of this program. It's always a good idea to set this out in English (or whatever language you think in) so that you know what you are aiming for:

1. Pick a word
2. Draw dashes (computer displays this)
3. Guess a letter (loop!)

If the letter is wrong, draw a body part
If the letter is correct, replace the dashes with the letter

The game play requires nested loop (iteration). For each turn the program wants to accept a letter guess, and then redraw the new state of the game. Drawing the state of the game usually means putting the gallows up with some part of the body; we can do this with "ASCII graphics" which is typing out on the console letters and/or special characters, so the result looks (more or less) like a picture. We programmers did this for years, long before we had graphics displays. It's a little tricky, so at first well play the game by counting the mistakes (and not drawing anything). And then the program types out (in another loop) dashes for the unguessed letters and substituting the actual letters that have been guessed correctly. A third (inner) loop compares each guessed letter to every letter in the word. And if you want the game to restart after each game ends, that would be another (outer) loop. We can do all of this inside the "StartHere" main() program we have been modifying.

We need to declare the data this game will be using. We could do some of this as String variables, but this is about arrays, so let's use arrays of char (character) for the word being guessed, and the displayed version, and also for the letters that have already been guessed.

Later we can build a constant array of String for the ASCII graphics of the gallows.

For now we need three character arrays, one for the word as given, one for the word as guessed so far, and one for the alphabet, the letters that have already been guessed. Here is a declaration for the alphabet array:

char[] alfa = new char[26];

You get to declare two more character arrays for the two words. I think I would be lazy and pick a fixed size -- say 32 -- and refuse to take any words longer than that. With a little more effort, you can look at the word you are given, then allocate as much space as you need for the whole word.

Next: Iteration in a Game

<<Previous | ToC | Next >>

Revised: 2020 September 18