Chapter 2
IN THIS CHAPTER
Working with basic one-dimensional arrays
Using array initializers to set the initial values of an array
Using for loops with arrays
Working with two-dimensional arrays
Working with the Arrays class
I could use a raise… .
Oh, arrays. Sorry.
Arrays are an important aspect of any programming language, and Java is no exception. In this chapter, you discover just about everything you need to know about using arrays. I cover run-of-the-mill one-dimensional arrays; multidimensional arrays; and two classes that are used to work with arrays, named Array
and Arrays
.
An array is a set of variables that is referenced by using a single variable name combined with an index number. Each item of an array is called an element. All the elements in an array must be of the same type. Thus the array itself has a type that specifies what kind of elements it can contain. An int
array can contain int
values, for example, and a String
array can contain strings.
The index number is written after the variable name and enclosed in brackets. So if the variable name is x
, you could access a specific element with an expression like x[5]
.
The real power of arrays comes from the simple fact that you can use a variable or even a complete expression as an array index. So (for example) instead of coding x[5]
to refer to a specific array element, you can code x[i]
to refer to the element indicated by the index variable i
. You see plenty of examples of index variables throughout this chapter.
Here are a few additional tidbits of array information to ponder before you get into the details of creating and using arrays:
x[5]
refers to an element of an array, x
refers to the array itself.0
to 9
.length
field of the array variable. x.length
, for example, returns the length of the array x
.Before you can create an array, you must declare a variable that refers to the array. This variable declaration should indicate the type of elements that are stored by the array followed by a set of empty brackets, like this:
String[] names;
Here a variable named names
is declared. Its type is an array of String
objects.
int[] array1; // an array of int elements
int array2[]; // another array of int elements
By itself, that statement doesn’t create an array; it merely declares a variable that can refer to an array. You can actually create the array in two ways:
new
keyword followed by the array type, this time with the brackets filled in to indicate how many elements the array can hold. For example:
String[] names;
names = new String[10];
Here, an array of String
objects that can hold ten strings is created. Each of the strings in this array is initialized to an empty string.
String[] names = new String[10];
Here the array variable is declared and an array is created in one statement.
String[] names = {"One", "Two", "Three"};
System.out.print("How many players? ");
int count = sc.nextInt(); // sc is a Scanner
String[] players = new String[count];
One way to initialize the values in an array is to simply assign them one by one:
String[] days = new Array[7];
Days[0] = "Sunday";
Days[1] = "Monday";
Days[2] = "Tuesday";
Days[3] = "Wednesday";
Days[4] = "Thursday";
Days[5] = "Friday";
Days[6] = "Saturday";
Java has a shorthand way to create an array and initialize it with values:
String[] days = { "Sunday", "Monday", "Tuesday",
"Wednesday", "Thursday",
"Friday", "Saturday" };
Here each element to be assigned to the array is listed in an array initializer. Here’s an example of an array initializer for an int
array:
int[] primes = { 2, 3, 5, 7, 11, 13, 17 };
Note: The length of an array created with an initializer is determined by the number of values listed in the initializer.
An alternative way to code an initializer is this:
int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17 };
To use this type of initializer, you use the new
keyword followed by the array type and a set of empty brackets. Then you code the initializer.
One of the most common ways to process an array is with a for
loop. In fact, for
loops were invented specifically to deal with arrays. Here’s a for
loop that creates an array of 100 random numbers, with values ranging from 1
to 100
:
int[] numbers = new int[100];
for (int i = 0; i < 100; i++)
numbers[i] = (int)(Math.random() * 100) + 1;
And here’s a loop that fills an array of player names with strings entered by the user:
String[] players = new String[count];
for (int i = 0; i < count; i++)
{
System.out.print("Enter player name: ");
players[i] = sc.nextLine(); // sc is a Scanner
}
For this example, assume that count
is an int
variable that holds the number of players to enter.
You can also use a for
loop to print the contents of an array. For example:
for (int i = 0; i < count; i++)
System.out.println(players[i]);
Here the elements of a String
array named players
are printed to the console.
The previous example assumes that the length of the array was stored in a variable before the loop was executed. If you don’t have the array length handy, you can get it from the array’s length
property:
for (int i = 0; i < players.length; i++)
System.out.println(players[i]);
Every once in a while, an array and a for
loop or two can help you solve your kids’ homework problems for them. I once helped my daughter solve a tough homework assignment for a seventh-grade math class. The problem was stated something like this:
Bobo (these problems always had a character named Bobo in them) visits the local high school on a Saturday and finds that all the school’s 1,000 lockers are neatly closed. So he starts at one end of the school and opens them all. Then he goes back to the start and closes every other locker (lockers 2, 4, 6, and so on). Then he goes back to the start and hits every third locker: If it’s open, he closes it; if it’s closed, he opens it. Then he hits every fourth locker, every fifth locker, and so on. He keeps doing this all weekend long, walking the hallways opening and closing lockers 1,000 times. Then he gets bored and goes home. How many of the school’s 1,000 lockers are left open, and which ones are they?
Sheesh!
This problem presented a challenge, and being the computer-nerd father I am, I figured that this was the time to teach my daughter about for
loops and arrays. So I wrote a little program that set up an array of 1,000 booleans. Each represented a locker: true
meant open, and false
meant closed. Then I wrote a pair of nested for
loops to do the calculation.
My first attempt told me that 10,000 of the 1,000 lockers were opened, so I figured that I’d made a mistake somewhere. And while I was looking at the code, I realized that the lockers were numbered 1 to 1,000, but the elements in my array were numbered 0
to 999
, and that was part of what led to the confusion that caused my first answer to be ridiculous.
So I decided to create the array with 1,001 elements and ignore the first one. That way, the indexes corresponded nicely to the locker numbers.
After a few hours of work, I came up with the program in Listing 2-1.
LISTING 2-1 The Classic Locker Problem Solved
public class BoboAndTheLockers
{
public static void main(String[] args)
{
// true = open; false = closed
boolean[] lockers = new boolean[1001];→6
// close all the lockers
for (int i = 1; i <= 1000; i++)→9
lockers[i] = false;
for (int skip = 1; skip <= 1000; skip++)→11
{
System.out.println("Bobo is changing every "
+ skip + " lockers.");
for (int locker = skip; locker < 1000;→15
locker += skip)
lockers[locker] = !lockers[locker];→17
}
System.out.println("Bobo is bored"
+ " now so he's going home.");
// count and list the open lockers
String list = "";
int openCount = 0;
for (int i = 1; i <= 1000; i++)→24
if (lockers[i])
{
openCount++;
list += i + " ";
}
System.out.println("Bobo left " + openCount
+ " lockers open.");
System.out.println("The open lockers are: "
+ list);
}
}
Here are the highlights of how this program works:
0
.for
loop closes all the lockers. This step isn’t really necessary because booleans initialize to false
, but being explicit about initialization is good.skip
variable represents how many lockers Bobo skips on each trip. First he does every locker, then every second locker, and then every third locker. So this loop simply counts from 1 to 1,000.for
statement (on the next line) adds the skip
variable to the index
variable so that Bobo can access every nth locker on each trip through the hallways.!
) to reverse the setting of each locker. Thus, if the locker is open (true
), it’s set to closed (false
), and vice versa.for
loop spins through all the lockers and counts the ones that are open. It also adds the locker number for each open locker to the end of a string so that all the open lockers can be printed.This program produces more than 1,000 lines of output, but only the last few lines are important. Here they are:
Bobo is bored now so he's going home.
Bobo left 31 lockers open.
The open lockers are: 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784 841 900 961
So there’s the answer: 31 lockers are left open. I got an A. (I mean, my daughter got an A.)
You can often eliminate the tedium of working with indexes in for
loops by using a special type of for
loop called an enhanced for
loop. Enhanced for
loops are often called for-each
loops because they automatically retrieve each element in a iterable object. Using a for-each
loop eliminates the need to create and initialize an index, increment the index, test for the last item in the array, and access each array item using the index.
When it's used with an array, the enhanced for
loop has this format:
for (type identifier : array)
{
Statements…
}
The type
identifies the type of the elements in the array, and the identifier
provides a name for a local variable that is used to access each element. The colon operator (often read as “in”) is then followed by the name of the array you want to process.
Here’s an example:
String[] days = { "Sunday", "Monday", "Tuesday",
"Wednesday", "Thursday",
"Friday", "Saturday" };
for (String day : days)
{
System.out.println(day);
}
This loop prints the following lines to the console:
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
In other words, it prints each of the strings in the array on a separate line.
It’s important to note that the for-each
loop gives you a copy of each item in the array, not the item itself. So, you can't alter the contents of an array by using a for-each
loop. Consider this code snippet:
int[] nums = {1, 2, 3, 4, 5};
for (int n : nums)
n = n * 2;
for (int n : nums)
System.out.println(n);
You may expect this code to print the values 2, 4, 6, 8, and 10 because you multiplied n
by 2 in the for-each
loop. But instead, the code prints 1, 2, 3, 4, and 5. That's because n
holds a copy of each array item, not the array item itself.
You can write methods that accept arrays as parameters and return arrays as return values. You just use an empty set of brackets to indicate that the parameter type or return type is an array. You’ve already seen this in the familiar main
method declaration:
public static void main(String[] args)
Here's a static method that creates and returns a String
array with the names of the days of the week:
public static String[] getDaysOfWeek()
{
String[] days = { "Sunday", "Monday", "Tuesday",
"Wednesday", "Thursday",
"Friday", "Saturday" };
return days;
}
And here’s a static method that prints the contents of any String
array to the console, one string per line:
public static void printStringArray(String[] strings)
{
for (String s : strings)
System.out.println(s);
}
Finally, here are two lines of code that call these methods:
String[] days = getDaysOfWeek();
printStringArray(days);
The first statement declares a String
array and then calls getDaysOfWeek
to create the array. The second statement passes the array to the printString Array
method as a parameter.
Varargs provides a convenient way to create a method that accepts a variable number of arguments. When you use varargs, the last argument in the method signature uses ellipses to indicate that the caller can provide one or more arguments of the given type.
Here’s an example:
public static void PrintSomeWords(String… words)
{
for (String word : words)
System.out.println(word);
}
Here, the PrintSomeWords
method specifies that the caller can pass any number of String arguments to the method (including none at all) and that the arguments will be gathered up in an array named words
.
Here's a snippet of code that calls the PrintSomeWords
methods using various numbers of arguments to prove the point:
PrintSomeWords();
PrintSomeWords("I");
PrintSomeWords("Am", "Not");
PrintSomeWords("Throwing", "Away", "My", "Shot");
The resulting console output looks like this:
I
Am
Not
Throwing
Away
My
Shot
An important caveat about using varargs is that the variable argument must always be the last argument in the argument list. This makes sense when you consider that, otherwise, the compiler wouldn’t be able to keep track of the arguments if any other than the last argument had a variable number. So, the following won’t compile:
public static void PrintSomeWords(String head, String… words, string tail)
You’ll get a warning that says, “Varargs parameter must be the last parameter.”
The elements of an array can be any type of object you want, including another array. In the latter case, you have a two-dimensional array, sometimes called an array of arrays.
Two-dimensional arrays are often used to track data in column-and-row format, much the way that a spreadsheet works. Suppose that you’re working on a program that tracks five years’ worth of sales (2016 through 2020) for a company, with the data broken down for each of four sales territories (North, South, East, and West). You could create 20 separate variables, with names such as sales2020North
, sales2020South
, sales2020East
, and so on. But that gets a little tedious.
Alternatively, you could create an array with 20 elements, like this:
double[] sales = new sales[20];
But then how would you organize the data in this array so that you know the year and sales region for each element?
With a two-dimensional array, you can create an array with an element for each year. Each of those elements in turn is another array with an element for each region.
Thinking of a two-dimensional array as a table or spreadsheet is common, like this:
Year |
North |
South |
East |
West |
2016 |
23,853 |
22,838 |
36,483 |
31,352 |
2017 |
25,483 |
22,943 |
38,274 |
33,294 |
2018 |
24,872 |
23,049 |
39,002 |
36,888 |
2019 |
28,492 |
23,784 |
42,374 |
39,573 |
2020 |
31,932 |
23,732 |
42,943 |
41,734 |
To declare a two-dimensional array for this sales data, you simply list two sets of empty brackets, like this:
double sales[][];
Here sales
is a two-dimensional array of type double
. To put it another way, sales
is an array of double
arrays.
To create the array, you use the new
keyword and provide lengths for each set of brackets, as in this example:
sales = new double[5][4];
Here the first dimension specifies that the sales
array has five elements. This array represents the rows in the table. The second dimension specifies that each of those elements has an array of type double
with four elements. This array represents the columns in the table.
Note that as with a one-dimensional array, you can declare and create a two-dimensional array in one statement, like this:
double[][] sales = new double[5][4];
Here the sales
array is declared and created all in one statement.
To access the elements of a two-dimensional array, you use two indexes. This statement sets the 2016 sales for the North region:
sales[0][0] = 23853.0;
As you might imagine, accessing the data in a two-dimensional array by hardcoding each index value can get tedious. No wonder for
loops are normally used instead. The following bit of code uses a for
loop to print the contents of the sales array to the console, separated by tabs. Each year is printed on a separate line, with the year at the beginning of the line. In addition, a line of headings for the sales regions is printed before the sales data. Here’s the code:
NumberFormat cf = NumberFormat.getCurrencyInstance();
System.out.println("\tNorth\t\tSouth\t\tEast\t\tWest");
int year = 2016;
for (int y = 0; y < 5; y++)
{
System.out.print(year + "\t");
for (int region = 0; region < 4; region++)
{
System.out.print(cf.format(sales[y][region]));
System.out.print("\t");
}
year++;
System.out.println();
}
Assuming that the sales
array has already been initialized, this code produces the following output on the console:
North South East West
2016 $23,853.00 $22,838.00 $36,483.00 $31,352.00
2017 $25,483.00 $22,943.00 $38,274.00 $33,294.00
2018 $24,872.00 $23,049.00 $39,002.00 $36,888.00
2019 $28,492.00 $23,784.00 $42,374.00 $39,573.00
2020 $31,932.00 $23,732.00 $42,943.00 $41,734.00
for (int region = 0; region < 4; region++)
{
for (int y = 0; y < 5; y++)
{
System.out.print(cf.format(sales[y][region]));
System.out.print(" ");
}
System.out.println();
}
Here the outer loop indexes the region and the inner loop indexes the year:
$23,853.00 $25,483.00 $24,872.00 $28,492.00 $31,932.00
$22,838.00 $22,943.00 $23,049.00 $23,784.00 $23,732.00
$36,483.00 $38,274.00 $39,002.00 $42,374.00 $42,943.00
$31,352.00 $33,294.00 $36,888.00 $39,573.00 $41,734.00
The technique for initializing arrays by coding the array element values in curly braces works for two-dimensional arrays too. You just have to remember that each element of the main array is actually another array. So you have to nest the array initializers.
Here’s an example that initializes the sales array:
double[][] sales =
{ {23853.0, 22838.0, 36483.0, 31352.0}, // 2004
{25483.0, 22943.0, 38274.0, 33294.0}, // 2005
{24872.0, 23049.0, 39002.0, 36888.0}, // 2006
{28492.0, 23784.0, 42374.0, 39573.0}, // 2007
{31932.0, 23732.0, 42943.0, 41734.0} }; // 2008
Here I added a comment to the end of each line to show the year that the line initializes. Notice that the left brace for the entire initializer is at the beginning of the second line, and the right brace that closes the entire initializer is at the end of the last line. Then the initializer for each year is contained in its own set of braces.
When you create an array with an expression such as new int[5][3]
, you’re specifying that each element of the main array is actually an array of type int
with three elements. Java, however, lets you create two-dimensional arrays in which the length of each element of the main array is different. This is sometimes called a jagged array because the array doesn’t form a nice rectangle. Instead, its edges are jagged.
Suppose that you need to keep track of four teams, each consisting of two or three people. The teams are as follows:
Team |
Members |
A |
Henry Blake, Johnny Mulcahy |
B |
Benjamin Pierce, John McIntyre, Jonathan Tuttle |
C |
Margaret Houlihan, Frank Burns |
D |
Max Klinger, Radar O’Reilly, Igor Straminsky |
The following code creates a jagged array for these teams:
String[][] teams
= { {"Henry Blake", "Johnny Mulcahy"},
{"Benjamin Pierce", "John McIntyre",
"Jonathan Tuttle"},
{"Margaret Houlihan", "Frank Burns"},
{"Max Klinger", "Radar O'Reilly",
"Igor Straminsky"} };
Here each nested array initializer indicates the number of strings for each subarray. The first subarray has two strings, the second has three strings, and so on.
You can use nested for
loops to access the individual elements in a jagged array. For each element of the main array, you can use the length
property to determine how many entries are in that element’s subarray. For example:
for (int i = 0; i < teams.length; i++)
{
for (int j = 0; j < teams[i].length; j++)
System.out.println(teams[i][j]);
System.out.println();
}
Notice that the length of each subarray is determined with the expression teams[i].length
. This for
loop prints one name on each line, with a blank line between teams, like this:
Margaret Houlihan
Frank Burns
Max Klinger
Radar O'Reilly
Igor Straminsky
Henry Blake
Johnny Mulcahy
Benjamin Pierce
John McIntyre
Jonathan Tuttle
If you don’t want to fuss with keeping track of the indexes yourself, you can use an enhanced for
loop and let Java take care of the indexes. For example:
for (String[] team : teams)
{
for (String player : team)
System.out.println(player);
System.out.println();
}
Here the first enhanced for
statement specifies that the type for the team
variable is String[]
. As a result, each cycle of this loop sets team
to one of the subarrays in the main teams
array. Then the second enhanced for
loop accesses the individual strings in each subarray.
int[][][] threeD = new int[3][3][3];
Here a three-dimensional array is created, with each dimension having three elements. You can think of this array as a cube. Each element requires three indexes to access.
You can access an element in a multidimensional array by specifying as many indexes as the array needs. For example:
threeD[0][1][2] = 100;
This statement sets element 2 in column 1 of row 0 to 100
.
You can nest initializers as deep as necessary, too. For example:
int[][][] threeD =
{ { {1, 2, 3}, { 4, 5, 6}, { 7, 8, 9} },
{ {10, 11, 12}, {13, 14, 15}, {16, 17, 18} },
{ {19, 20, 21}, {22, 23, 24}, {25, 26, 27} } };
Here a three-dimensional array is initialized with the numbers 1
through 27
.
You can also use multiple nested if
statements to process an array with three or more dimensions. Here’s another way to initialize a three-dimensional array with the numbers 1
to 27
:
int[][][] threeD2 = new int[3][3][3];
int value = 1;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
threeD2[i][j][k] = value++;
Okay, so much for the business examples. Here’s an example that’s more fun — assuming you think chess is fun. The program in Listing 2-2 uses a two-dimensional array to represent a chessboard. Its sole purpose is to figure out the possible moves for a knight (that’s the horsey, for the non-chess players among us), given its starting position. The user is asked to enter a starting position (such as f1
), and the program responds by displaying the possible squares. Then the program prints out a crude-but-recognizable representation of the board, with the knight’s position indicated with an X
and each possible move indicated with a question mark (?
).
Here’s a sample of what the console looks like if you enter e4
for the knight’s position:
Welcome to the Knight Move calculator.
Enter knight's position: e4
The knight is at square e4
From here the knight can move to:
c5
d6
f6
g5
g3
f2
d2
c3
- - - - - - - -
- - - - - - - -
- - - ? - ? - -
- - ? - - - ? -
- - - - X - - -
- - ? - - - ? -
- - - ? - ? - -
- - - - - - - -
Do it again? (Y or N) n
As you can see, the program indicates that the knight’s legal moves from e4
are c5
, d6
, f6
, g5
, g3
, f2
, d2
, and c3
. Also, the graphic representation of the board indicates where the knight is and where he can go.
LISTING 2-2 Playing Chess in a For Dummies Book
import java.util.Scanner;
public class KnightMoves
{
static Scanner sc = new Scanner(System.in);
// the following static array represents the 8
// possible moves a knight can make
// this is an 8 x 2 array
static int[][] moves = { {-2, +1},→10
{-1, +2},
{+1, +2},
{+2, +1},
{+2, -1},
{+1, -2},
{-1, -2},
{-2, -1} };
public static void main(String[] args)
{
System.out.println("Welcome to the "
+ "Knight Move calculator.\n");
do
{
showKnightMoves();→25
}
while (getYorN("Do it again?"));
}
public static void showKnightMoves()→29
{
// The first dimension is the file (a, b, c, etc.)
// The second dimension is the rank (1, 2, 3, etc.)
// Thus, board[3][4] is square d5.
// A value of 0 means the square is empty
// 1 means the knight is in the square
// 2 means the knight could move to the square
int[][] board = new int[8][8];→37
String kSquare; // the knight's position as a square
Pos kPos; // the knight's position as a Pos
// get the knight's initial position
do→43
{
System.out.print("Enter knight's position: ");
kSquare = sc.nextLine();
kPos = convertSquareToPos(kSquare);
} while (kPos == null);
board[kPos.x][kPos.y] = 1;→50
System.out.println("\nThe knight is at square "
+ convertPosToSquare(kPos));
System.out.println(
"From here the knight can move to:");
for (int move = 0; move < moves.length; move ++)→55
{
int x, y;
x = moves[move][0]; // the x for this move
y = moves[move][1]; // the y for this move
Pos p = calculateNewPos(kPos, x, y);
if (p != null)
{
System.out.println(convertPosToSquare(p));
board[p.x][p.y] = 2;
}
}
printBoard(board);→68
}
// this method converts squares such as a1 or d5 to
// x, y coordinates such as [0][0] or [3][4]
public static Pos convertSquareToPos(String square)→73
{
int x = -1;
int y = -1;
char rank, file;
file = square.charAt(0);
if (file == 'a') x = 0;
if (file == 'b') x = 1;
if (file == 'c') x = 2;
if (file == 'd') x = 3;
if (file == 'e') x = 4;
if (file == 'f') x = 5;
if (file == 'g') x = 6;
if (file == 'h') x = 7;
rank = square.charAt(1);
if (rank == '1') y = 0;
if (rank == '2') y = 1;
if (rank == '3') y = 2;
if (rank == '4') y = 3;
if (rank == '5') y = 4;
if (rank == '6') y = 5;
if (rank == '7') y = 6;
if (rank == '8') y = 7;
if (x == -1 || y == -1)
{
return null;
}
else
return new Pos(x, y);
}
// this method converts x, y coordinates such as
// [0][0] or [3][4] to squares such as a1 or d5.
public static String convertPosToSquare(Pos p)→109
{
String file = "";
if (p.x == 0) file = "a";
if (p.x == 1) file = "b";
if (p.x == 2) file = "c";
if (p.x == 3) file = "d";
if (p.x == 4) file = "e";
if (p.x == 5) file = "f";
if (p.x == 6) file = "g";
if (p.x == 7) file = "h";
return file + (p.y + 1);
}
// this method calculates a new Pos given a
// starting Pos, an x move, and a y move
// it returns null if the resulting move would
// be off the board.
public static Pos calculateNewPos(Pos p, int x, int y)→129
{
// rule out legal moves
if (p.x + x < 0)
return null;
if (p.x + x > 7)
return null;
if (p.y + y < 0)
return null;
if (p.y + y > 7)
return null;
// return new position
return new Pos(p.x + x, p.y + y);
}
public static void printBoard(int[][] b)→145
{
for (int y = 7; y >= 0; y--)
{
for (int x = 0; x < 8; x++)
{
if (b[x][y] == 1)
System.out.print(" X ");
else if (b[x][y] == 2)
System.out.print(" ? ");
else
System.out.print(" - ");
}
System.out.println();
}
}
public static boolean getYorN(String prompt)→161
{
while (true)
{
String answer;
System.out.print("\n" + prompt + " (Y or N) ");
answer = sc.nextLine();
if (answer.equalsIgnoreCase("Y"))
return true;
else if (answer.equalsIgnoreCase("N"))
return false;
}
}
}
// this class represents x, y coordinates on the board
class Pos→177
{
public int x;
public int y;
public Pos(int x, int y)
{
this.x = x;
this.y = y;
}
}
showKnightMoves
. That way, the do
loop in the main method is kept simple. It just keeps going until the user enters N
when getYorN
is called.showKnightMoves
method begins here.do
loop prompts the user for a valid square to plant the knight in. The loop includes a call to the method convertSquareToPos
, which converts the user’s entry (such as e4
) to a Pos
object. (The Pos
class is defined later in the program; it represents a board position as an x, y pair.) This method returns null if the user enters an incorrect square, such as a9
or x4
. So to get the user to enter a valid square, the loop just repeats if the converSquareToPos
returns null.for
loop is used to test all the possible moves for the knight to see whether they’re valid from the knight’s current position, using the moves array that was created way back in line 10. In the body of this loop, the calculateNewPos
method is called. This method accepts a board position and x and y values to indicate where the knight can be moved. If the resulting move is legal, it returns a new Pos
object that indicates the position the move leads to. If the move is not legal (that is, it takes the knight off the board), the calculateNewPos
method returns null.
Assuming that calculateNewPos
returns a non-null value, the body of this loop prints the square (it calls convertPosTosquare
to convert the Pos
object to a string, such as c3
). Then it marks the board position represented by the move with 2 to indicate that the knight can move to this square.
printBoard
method is called to print the board array.convertSquareToPos
method. It uses a pair of brute-force if statements to convert a string such as a1
or e4
to a Pos
object representing the same position. I probably could have made this method a little more elegant by converting the first letter in the string to a Char
and then subtracting the offset of the letter a
to convert the value to a proper integer. But I think the brute-force method is clearer, and it requires only a few more lines of code.Note that if the user enters an incorrect square (such as a9
or x2
), null is returned.
convertPosToSquare
method, which does the opposite of the convertSquareToPos
method. It accepts a Pos
argument and returns a string that corresponds to the position. It uses a series of brute-force if statements to determine the letter that corresponds to the file but does a simple addition to calculate the rank. (The Pos
object’s y
member is an array for the y
position. Array indexes are numbered starting with 0, but chess rank numbers start with 1. That’s why 1 is added to the y
position to get the rank number.)calculateNewPos
method accepts a starting position, an x
offset, and a y
offset. It returns a new position if the move is legal; otherwise it returns null. To find illegal moves, it adds the x
and y
offsets to the starting x
and y
position and checks to see whether the result is less than 0 or greater than 7. If the move is legal, it creates a new Pos
object whose position is calculated by adding the x
and y
offsets to the x
and y
values of the starting position.printBoard
method uses a nested for loop to print the board. The outer loop prints each rank. Notice that it indexes the array backward, starting with 7 and going down to 0. That’s necessary so that the first rank is printed at the bottom of the console output. An inner for loop is used to print the squares for each rank. In this loop, an if statement checks the value of the board array element that corresponds to the square to determine whether it prints an X, a question mark, or a hyphen.getYorN
method simply displays a prompt on-screen and asks the user to enter Y
or N
. It returns true
if the user enters Y
or false
if the user enters N
. If the user enters anything else, this method prompts the user again.Pos
class simply defines two public fields, x
and y
, to keep track of board positions. It also defines a constructor that accepts the x
and y
positions as parameters.The final topic for this chapter is the Arrays
class, which provides a collection of static
methods that are useful for working with arrays. The Arrays
class is in the java.util
package, so you have to use an import
statement for the java.util.Arrays
class or the entire java.util.*
package to use this class. Table 2-1 lists the most commonly used methods of the Arrays
class.
TABLE 2-1 Handy Methods of the Arrays Class
Method |
Description |
|
Searches for the specified key value in an array. The return value is the index of the element that matches the key. The method returns |
|
Returns an array that’s a copy of |
|
Does basically what the |
|
Returns |
|
Returns |
|
Fills the array with the specified value. The value and array must be of the same type and can be any primitive type or an object. |
|
Fills the elements indicated by the |
|
Sets the elements of an array using a function provided by generator, usually written with a lambda expression. |
|
Sorts the array in ascending sequence. |
|
Sorts the specified elements of the array in ascending sequence. |
|
Formats the array values in a string. Each element value is enclosed in brackets, and the element values are separated with commas. |
The fill
method can be handy if you want to prefill an array with values other than the default values for the array type. Here’s a routine that creates an array of integers and initializes each element to 100
:
int[] startValues = new int[10];
Arrays.fill(startValues, 100);
You might think that you could fill an array of 1,000 integers with random numbers from 1 to 100, like this:
int[] ran = new int[1000]
Arrays.fill(ran, (int)(Math.random() * 100) + 1);
Unfortunately, this code won’t work. What happens is that the expression is evaluated once to get a random number; then all 1,000 elements in the array are set to that random number.
int[] a = new int[10];
Arrays.setAll(a, i -> (int)(Math.random() * 100) + 1);
In Java 1.6, the Arrays
class has some useful new methods. Using the new copyOf
and copyOfRange
methods, you can copy a bunch of elements from an existing array into a brand-new array. If you start with something named arrayOriginal
, for example, you can copy the arrayOriginal
elements to something named arrayNew
, as shown in Listing 2-3.
LISTING 2-3 The Copycat
import java.util.Arrays;
class CopyDemo
{
public static void main(String args[])
{
int arrayOriginal[] = {42, 55, 21};
int arrayNew[] =
Arrays.copyOf(arrayOriginal, 3);→9
printIntArray(arrayNew);
}
static void printIntArray(int arrayNew[])
{
for (int i : arrayNew)
{
System.out.print(i);
System.out.print(' ');
}
System.out.println();
}
}
The output of the CopyDemo
program looks like this:
42 55 21
Line 9 is where the array is actually copied. Here, the number 3 specifies how many array elements to copy.
If you want, you can copy less than the full array. For example:
int arrayNew[] = Arrays.copyOf(arrayOriginal, 2);
Then, arrayNew
has just two elements:
42 55
You can also copy more than the full array, in which case, additional elements are added with default values. For example:
int arrayNew[] = Arrays.copyOf(arrayOriginal, 8);
Then arrayNew
has eight elements:
42 55 21 0 0 0 0 0
The copyOfRange
method is even more versatile. If you execute the instructions
int arrayOriginal[] = {42, 55, 21, 16, 100, 88};
int arrayNew[] = Arrays.copyOfRange(arrayOriginal, 2, 5);
the values in arrayNew
are
21 16 100
The sort
method is a quick way to sort an array in sequence. These statements create an array with 100 random numbers and then sort the array in sequence so that the random numbers are in order:
int[] lotto = new int[6];
for (int i = 0; i < 6; i++)
lotto[i] = (int)(Math.random() * 100) + 1;
Arrays.sort(lotto);
The binarySearch
method is an efficient way to locate an item in an array by its value. Suppose you want to find out whether your lucky number is in the lotto
array created in the preceding example. You could just use a for
loop, like this:
int lucky = 13x;
int foundAt = -1;
for (int i = 0; i < lotto.length; i++)
if (lotto[i] == lucky)
foundAt = i;
if (foundAt > -1)
System.out.println("My number came up!");
else
System.out.println("I'm not lucky today.");
Here the for
loop compares each element in the array with your lucky number. This code works fine for small arrays, but what if the array had 1,000,000 elements instead of 6? In that case, it would take a while to look at each element. If the array is sorted in sequence, the binarySearch
method can find your lucky number more efficiently and with less code:
int lucky = 13;
int foundAt = Arrays.binarySearch(lotto, lucky);
if (foundAt > -1)
System.out.println("My number came up!");
else
System.out.println("I'm not lucky today.");
If you use the equality operator (==
) to compare array variables, the array variables are considered to be equal only if both variables point to exactly the same array instance. To compare two arrays element by element, you should use the Arrays.equals
method instead. For example:
if (Arrays.equals(array1, array2))
System.out.println("The arrays are equal!");
Here the arrays array1
and array2
are compared element by element. If both arrays have the same number of elements, and all elements have the same value, the equals
method returns true
. If the elements are not equal, or if one array has more elements than the other, the equals
method returns false
.
The toString
method of the Arrays
class is handy if you want to quickly dump the contents of an array to the console to see what it contains. This method returns a string that shows the array’s elements enclosed in brackets, with the elements separated by commas.
Here’s a routine that creates an array, fills it with random numbers, and then uses the toString
method to print the array elements:
int[] lotto = new int[6];
for (int i = 0; i < 6; i++)
lotto[i] = (int)(Math.random() * 100) + 1;
System.out.println(Arrays.toString(lotto));
Here’s a sample of the console output created by this code:
[4, 90, 65, 84, 99, 81]