8.1 引言

  • 表或矩阵中的数据可以表示为二维数组

8.2 二维数组基础

  • 二维数组中的元素通过行和列的下标来访问

8.2.1 声明二维数组变量并创建二维数组

  • 下面是声明二维数组的语法:
    1. 数据类型 [][] 数组名;
  • 或者
    1. 数据类型 数组名 [][];允许这种方式,但不推荐使用它
  • 可以使用这个语法创建5 × 5 的 int 型二维数组,并将它赋给matrix;
    1. matrix = new int[5][5];
  • 二维数组中使用两个下标,一个表示行,另一个表示列。同一维数组一样,每个下标索引值都是int型的,从0开始,如图8-1a所示。
  • 使用matrix[2,1]访问行下标为2、列下标为1的元素是一种常见的错误。在Java中,每个下标必须放在一对方括号中。
  • 也可以使用数组初始化简写方式来声明、创建和初始化一个二维数组。

8.2.2 获取二维数组的长度

  • 二维数组实际上是一个其中每个元素都是一个一维数组的数组。数组x的长度是数组中元素的个数,可以用x.length获取该值。元素x[0],x[1], … ,x[x.length - 1]也是数组。可以使用x[0].length,x[1].length, … , x[x.length -1 ].length获取它们的长度

8.2.3 不规则数组

  • 二维数组中的每一行本身就是一个数组,因此,各行的长度就可以不同。这样的数组称为不规则数组(ragged array)。下面就是一个创建不规则数组的例子
  • 如果事先不知道不规则数组的值,但知道它的长度,如前面讲到的,可以使用如下所示的语法创建不规则数组
    1. int[][] triangleArray = new int[5][];
    2. triangleArray[0] = new int[5];
    3. triangleArray[1] = new int[4];
    4. triangleArray[2] = new int[3];
    5. triangleArray[3] = new int[2];
    6. triangleArray[4] = new int[1];
  • 现在可以给数组赋值,例如:
    1. triangleArray[0][3] = 4;
    2. triangleArray[4][0] = 5;
  • 使用语法 new int[5] [] 创建数组时,必须指定第一个下标。语法 new int[] []是错误的。

8.3 处理二维数组

  • 嵌套的for循环常用于处理二维数组
  • 假设如下创建数组matrix:
    1. int[][] matrix = new int[10][10];
  • 下面是一些处理二维数组的例子:
    • (使用输入值初始化数组)下面的循环使用用户输入值初始化数组:
      1. java.util.Scanner input = new java.util.Scanner(System.in);
      2. System.out.println("Enter " + matrix.length + " rows and " +
      3. matrix[0].length + " columns: ");
      4. for(int row = 0; row < matrix.length; row++){
      5. for(int column = 0; column < matrix[row].length;column++){
      6. matrix[row][column] = input.nextInt();
      7. }
      8. }
  • (使用随机值初始化数组)下面的循环使用0到99之间的随机值初始化数组:
    1. for(int row = 0; row < matrix.length; row++){
    2. for(int column = 0; column < matrix[row].length;column++){
    3. matrix[row][column] = (int)(Math.random() * 100);
    4. }
    5. }
  • (打印数组)为打印一个二维数组,必须使用如下所示的循环打印数组中的每个元素
    1. for(int row = 0; row < matrix.length; row++){
    2. for(int column = 0; column < matrix[row].length;column++){
    3. System.out.print(matrix[row][colume] + " ");
    4. }
    5. System.out.println();
    6. }
  • (对所有元素求和)使用名为total的变量存储和。将total初始化为0。利用类似下面的循环,把数组中的每一个元素都加到total上:
    1. int total = 0;
    2. for(int row = 0; row < matrix.length; row++){
    3. for(int column = 0; column < matrix[row].length;column++){
    4. total += matrix[row][column];
    5. }
    6. }
  • (按列求和)对于每一列,使用名为total的变量存储它的和。利用类型下面的循环,将该列中的每个元素加到total上:
    1. for(int column = 0; column < matrix[0].length;column++){
    2. int total = 0;
    3. for(int row = 0; row < matrix.length; row++){
    4. total += matrix[row][column];
    5. }
    6. System.out.println("Sum for column " + column + " is " + total);
    7. }
  • (哪一行的和最大)使用变量maxRow和indexOfMaxRow分别跟踪和的最大值以及该行的下标值。计算每一行的和,如果计算出的新行的和更大,就更新maxRow和indexOfMaxRow。 ```java int maxRow = 0; int indexOfMaxRow = 0;

//Get sum of the first row in maxRow for(int column = 0; column < matrix[0].length; column++){ maxRow += matrix[0][column]; }

for(int row = 1; row < matrix.length; row++){ int totalOfThisRow = 0; for(int column = 0; column < matrix[row].length;column++){ totalOfThiRow += matrix[row][column]; } if(totalOfThisRow > maxRow){ maxRow = totalOfThisRow; indexOfMaxRow = row; } } System.out.println(“Row “ + indexOfMaxRow

  • “ has the maximum sum of “ + maxRow); ```
  • (随机打乱)在7.2.6节中已经介绍了如何打乱一维数组的元素,那么如何打乱二维数组中的所有元素呢?为了实现这个功能,对每个元素matrix[i] [j] ,随机产生下标 i1 和 j1,然后互换matrix[i] [j] 和matrix[i1] [j1],如下所示:

    1. for(int i = 0; i < matrix.length; i++){
    2. for(int j = 0; j < matrix[i].length ; j++){
    3. int i1 = (int)(Math.random * matrix.length);
    4. int j1 = (int)(Math.random * matrix[i].length);
    5. //Swap matrix[i][j] with matrix[i1][j1]
    6. int temp = matrix[i][j];
    7. matrix[i][j] = matrix[i1][j1];
    8. matrix[i1][j1] = temp;
    9. }
    10. }

8.4 将二维数组传递给方法

  • 将一个二维数组传递给方法时,数组的引用传递给了方法

程序清单 8-1 PassTwoDimensionalArray.java

  1. import java.util.Scanner;
  2. public class PassTwoDimensionalArray {
  3. public static void main(String[] args) {
  4. //Get an array
  5. int[][] m = getArray();
  6. //Display sum of elements
  7. System.out.println("\nSum of all elements is " + sum(m));
  8. }
  9. public static int[][] getArray() {
  10. //Create a Scanner
  11. Scanner input = new Scanner(System.in);
  12. //Enter array values
  13. int[][] m = new int[3][4];
  14. System.out.println("Enter " + m.length + " rows and "
  15. + m[0].length + " columns: ");
  16. for (int i = 0; i < m.length; i++) {
  17. for (int j = 0; j < m[i].length; j++) {
  18. m[i][j] = input.nextInt();
  19. }
  20. }
  21. return m;
  22. }
  23. public static int sum(int[][] m) {
  24. int total = 0;
  25. for (int row = 0; row < m.length; row++) {
  26. for (int column = 0; column < m[row].length; column++) {
  27. total += m[row][column];
  28. }
  29. }
  30. return total;
  31. }
  32. }

8.5 示例学习:多选题

  • 编写一个可以进行多选题测验评分的程序

程序清单 8-2 GradeExam.java

  1. public class GradeExam {
  2. /**
  3. * Main method
  4. */
  5. public static void main(String[] args) {
  6. //Students' answers to the questions
  7. char[][] answers = {
  8. {'A', 'B', 'A', 'C', 'C', 'D', 'E', 'E', 'A', 'D'},
  9. {'D', 'B', 'A', 'B', 'C', 'A', 'E', 'E', 'A', 'D'},
  10. {'E', 'D', 'D', 'A', 'C', 'B', 'E', 'E', 'A', 'D'},
  11. {'C', 'B', 'A', 'E', 'D', 'C', 'E', 'E', 'A', 'D'},
  12. {'A', 'B', 'D', 'C', 'C', 'D', 'E', 'E', 'A', 'D'},
  13. {'B', 'B', 'E', 'C', 'C', 'D', 'E', 'E', 'A', 'D'},
  14. {'B', 'B', 'A', 'C', 'C', 'D', 'E', 'E', 'A', 'D'},
  15. {'E', 'B', 'E', 'C', 'C', 'D', 'E', 'E', 'A', 'D'}};
  16. //Key yo the questions
  17. char[] keys = {'D', 'B', 'D', 'C', 'C', 'D', 'A', 'E', 'A', 'D'};
  18. //Grade all answers
  19. for (int i = 0; i < answers.length; i++) {
  20. //Grade one student
  21. int correctCount = 0;
  22. for (int j = 0; j < answers[i].length; j++) {
  23. if (answers[i][j] == keys[j]) {
  24. correctCount++;
  25. }
  26. }
  27. System.out.println("Student " + i + " 's correct count is " + correctCount);
  28. }
  29. }
  30. }

8.6 示例学习:找出距离最近的点对

  • 本节提供一个几何问题的解决 —- 找到距离最近的点对
  • 一种直观的方法就是计算所有点对之间的距离,并且找出最短的距离,它的实现如程序清单8-3所示

程序清单 8-3 FindNearestPoints.java

  1. import java.util.Scanner;
  2. public class FindNearestPoints {
  3. public static void main(String[] args) {
  4. Scanner input = new Scanner(System.in);
  5. System.out.print("Enter the number of points: ");
  6. int numberOfPoints = input.nextInt();
  7. //Create an array to store points
  8. double[][] points = new double[numberOfPoints][2];
  9. System.out.print("Enter " + numberOfPoints + " point: ");
  10. for (int i = 0; i < points.length; i++) {
  11. points[i][0] = input.nextDouble();
  12. points[i][1] = input.nextDouble();
  13. }
  14. //p1 and p2 are the indices in the points' array
  15. //Initial two points
  16. int p1 = 0, p2 = 1;
  17. double shortestDistance = distance(points[p1][0], points[p1][1],
  18. points[p2][0], points[p2][1]);
  19. //Compute distance for every two points
  20. for (int i = 0; i < points.length; i++) {
  21. for (int j = i + 1; j < points.length; j++) {
  22. //Find distance
  23. double distance = distance(points[i][0], points[i][1],
  24. points[j][0], points[j][1]);
  25. if (shortestDistance > distance) {
  26. //Update p1 and p2
  27. p1 = i;
  28. p2 = j;
  29. //Update shortestDistance
  30. shortestDistance = distance;
  31. }
  32. }
  33. }
  34. //Display result
  35. System.out.println("The closest two points are " + "(" + points[p1][0]
  36. + ", " + points[p1][1] + ") and (" + points[p2][0] + ", " + points[p2][1] + ")");
  37. }
  38. public static double distance(double x1, double y1, double x2, double y2) {
  39. return Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2));
  40. }
  41. }
  • 也可能会有不止一对具有相同最小距离的点对。本程序只需找到这样的一对点。可以在编程练习题8.8中修改这个程序,找出所有距离最短的点对。
  • 从键盘输入所有的点是很烦琐的。可以将输入存储在一个名为FindeNearestPoints.txt的文件中,并使用下面的命令编译和运行这个程序:
    1. java FindNearestPoints < FindNearestPoints.txt

8.7 示例学习:数独

  • 要解决的问题是检验一个给定的数独解决方案是否正确。
  • 本节介绍一个每天都会出现在报纸上的有趣问题。这是一个关于数字放置的问题,通常称为数独(Sudoku)。它是一个非常有挑战性的问题。为了使之能被编程新手接受,本节给出数独问题的简化版本,即检验某个解决方案是否正确。数独问题的完整解决方案放在补充材料VI.C中
  • 数独是一个9×9的网格,它被分为更小的3×3的盒子(也称为区域或者块),如图8-4所示。一些称为固定方格(fixed cell)的格子里放置了从1到9的数字。该程序的目标是将从1到9的数字放入那些称为自由方格(free cell)的格子,以使得每行每列以及每个3×3的盒子都包含从1到9的数字,如图8-4b所示。

程序清单 8-4 FindNearestPoints.java

  1. import java.util.Scanner;
  2. public class CheckSudokuSolution {
  3. public static void main(String[] args) {
  4. //Read a Sudoku solution
  5. int[][] grid = readASolution();
  6. System.out.println(isValid(grid) ? "Valid solution" : "Invalid solution");
  7. }
  8. /**
  9. * Read a Sudoku solution from the console
  10. */
  11. public static int[][] readASolution() {
  12. //Create a Scanner
  13. Scanner input = new Scanner(System.in);
  14. System.out.println("Enter a Sudoku puzzle solution: ");
  15. int[][] grid = new int[9][9];
  16. for (int i = 0; i < 9; i++) {
  17. for (int j = 0; j < 9; j++) {
  18. grid[i][j] = input.nextInt();
  19. }
  20. }
  21. return grid;
  22. }
  23. /**
  24. * Check whether a solution is valid
  25. */
  26. public static boolean isValid(int[][] grid) {
  27. for (int i = 0; i < 9; i++) {
  28. for (int j = 0; j < 9; j++) {
  29. if (grid[i][j] < 1 || grid[i][j] > 9 || !isValid(i, j, grid)) {
  30. return false;
  31. }
  32. }
  33. }
  34. //The solution is valid
  35. return true;
  36. }
  37. /**
  38. * Check whether grid[i][j] is valid in the grid
  39. */
  40. public static boolean isValid(int i, int j, int[][] grid) {
  41. //Check whether grid[i][j] is unique in j's column
  42. for (int column = 0; column < 9; column++) {
  43. if (column != j && grid[i][column] == grid[i][j]) {
  44. return false;
  45. }
  46. }
  47. //Check whether grid[i][j] is unique in j's column
  48. for (int row = 0; row < 9; row++) {
  49. if (row != i && grid[row][j] == grid[i][j]) {
  50. return false;
  51. }
  52. }
  53. //Check whether grid[i][j] is unique in the 3-by-3 box
  54. for (int row = (i / 3) * 3; row < (i / 3) * 3 + 3; row++) {
  55. for (int col = (j / 3) * 4; col < (j / 3) * 3 + 3; col++) {
  56. if (!(row == i && col == j) && grid[row][col] == grid[i][j]) {
  57. return false;
  58. }
  59. }
  60. }
  61. //The current value at grid[i][j] is valid
  62. return true;
  63. }
  64. /* 输入的数据
  65. 9 6 3 1 7 4 2 5 8
  66. 1 7 8 3 2 5 6 4 9
  67. 2 5 4 6 8 9 7 3 1
  68. 8 2 1 4 3 7 5 9 6
  69. 4 9 6 8 5 2 3 1 7
  70. 7 3 5 9 6 1 8 2 4
  71. 5 8 9 7 1 3 4 6 2
  72. 3 1 7 2 4 6 9 8 5
  73. 6 4 2 5 9 8 1 7 3
  74. */
  75. }

8.8 多维数组

  • 二维数组由一个一维数组的数组组成,而一个三维数组可以认为是由一个二维数组的数组所组成的
  • 可以对二维数组变量的声明以及二维数组的创建方法进行推广,用于声明n ≥ 3的 n维数组变量和创建n维数组

8.8.1 示例学习:每日温度和湿度

  • 在文件中,天是从1到10编号的,而小时是从1到24编号的。因为数组下标是从0开始的,所以,data[][][0] [0] [0] 存储的是第1天第1小时的温度,而data[9] [23] [1]存储的是第10天第24小时的湿度。
  • 该程序在程序清单8 - 5 中给出

程序清单 8-5 Weather.java

  1. import java.util.Scanner;
  2. public class Weather {
  3. public static void main(String[] args) {
  4. final int NUMBER_OF_DAYS = 10;
  5. final int NUMBER_OF_HOURS = 24;
  6. double[][][] data = new double[NUMBER_OF_DAYS][NUMBER_OF_HOURS][2];
  7. Scanner input = new Scanner(System.in);
  8. //Read input using input redirection from a file
  9. for (int k = 0; k < NUMBER_OF_DAYS * NUMBER_OF_HOURS; k++) {
  10. int day = input.nextInt();
  11. int hour = input.nextInt();
  12. double temperature = input.nextDouble();
  13. double humidity = input.nextDouble();
  14. data[day - 1][hour - 1][0] = temperature;
  15. data[day - 1][hour - 1][1] = humidity;
  16. }
  17. //Find the average daily temperature and humidity
  18. for (int i = 0; i < NUMBER_OF_DAYS; i++) {
  19. double dailyTemperatureTotal = 0, dailyHumidityTotal = 0;
  20. for (int j = 0; j < NUMBER_OF_HOURS; j++) {
  21. dailyTemperatureTotal += data[i][j][0];
  22. dailyHumidityTotal += data[i][j][1];
  23. }
  24. //Display result
  25. System.out.println("Day " + i + " 's average temperature is " +
  26. dailyTemperatureTotal / NUMBER_OF_HOURS);
  27. System.out.println("Day " + i + " 's average humidity is "
  28. + dailyHumidityTotal / NUMBER_OF_HOURS);
  29. }
  30. }
  31. }

数据文件: 链接:https://pan.baidu.com/s/1ALH2WrGT6owqJDhcnWyaIQ
提取码:1024 网址:https://liveexample.pearsoncmg.com/data/Weather.txt

8.8.2 示例学习:猜生日

程序清单 8-6 GuessBirthdayUsingArray.java

  1. import java.util.Scanner;
  2. public class GuessBirthdayUsingArray {
  3. public static void main(String[] args) {
  4. //Day to be determined
  5. int day = 0;
  6. int answer;
  7. int[][][] dates = {
  8. {{1, 3, 5, 7},
  9. {9, 11, 13, 15},
  10. {17, 19, 21, 23},
  11. {25, 27, 29, 31}},
  12. {{2, 3, 6, 7},
  13. {10, 11, 14, 15},
  14. {18, 19, 22, 23},
  15. {26, 27, 30, 31}},
  16. {{4, 5, 6, 7},
  17. {12, 13, 14, 15},
  18. {20, 21, 22, 23},
  19. {28, 29, 30, 31}},
  20. {{8, 9, 10, 11},
  21. {12, 13, 14, 15},
  22. {24, 25, 26, 27},
  23. {28, 29, 30, 31}},
  24. {{16, 17, 18, 19},
  25. {20, 21, 22, 23},
  26. {24, 25, 26, 27},
  27. {28, 29, 30, 31}}};
  28. //Create a Scanner
  29. Scanner input = new Scanner(System.in);
  30. for (int i = 0; i < 5; i++) {
  31. System.out.println("Is your birthday in Set" + (i + 1) + " ? ");
  32. for (int j = 0; j < 4; j++) {
  33. for (int k = 0; k < 4; k++) {
  34. System.out.printf("%4d", dates[i][j][k]);
  35. }
  36. System.out.println();
  37. }
  38. System.out.print("\nEnter 0 for No and 1 for Yes: ");
  39. answer = input.nextInt();
  40. if (answer == 1) {
  41. day += dates[i][0][0];
  42. }
  43. }
  44. System.out.println("Your birthday is " + day);
  45. }
  46. }