数组

概述

数组是一个引用类型, 是一种容器.

数组存储多个相同数据类型的数据, 允许自动类型转换. 例如 int 类型的数组, 可以存放 byte, short 和 int 类型的数据, double 类型的数组, 可以存放 byte, short, int, long, float 和 double 类型的数据. boolean 类型的数据不能存放进 int 类型或者 double 类型的数组中.

程序示例:

public class Array {
    public static void main(String[] args) {
        double[] hens = {1, 2, 3};   // 定义数组
        // 数组名为 hens, 数组的类型为 double[], 数组中的元素的类型为 double
        // 遍历数组
        for (int i = 0; i < hens.length; i++) {
            System.out.println(hens[i]);
        }
    }
}

数组定义之后, 长度就固定了, 不能再发生变化.

获取数组元素个数: 数组名.length

数组名是一个地址:

public static void main(String[] args) {
    double[] double1 = {1.1, 2.2, 3, 4, 5};
    System.out.println(double1);            // [D@119d7047
    float[] float1 = {1.1F, 2.2F, 3, 4, 5};
    System.out.println(float1);             // [F@776ec8df
    long[] long1 = {1, 2, 3};
    System.out.println(long1);              // [J@4eec7777
    int[] int1 = {1, 2, 3};
    System.out.println(int1);               // [I@3b07d329
    short[] short1 = {1, 2, 3};
    System.out.println(short1);             // [S@41629346
    byte[] byte1 = {1, 2, 3};
    System.out.println(byte1);              // [B@404b9385
    boolean[] boolean1 = {false, false, true, false, true};
    System.out.println(boolean1);           // [Z@6d311334
    char[] char1 = {'1', '2', '3'};
    System.out.println(char1);              // 123
    String[] String1 = {"Hello", "World"};
    System.out.println(String1);            // [Ljava.lang.String;@682a0b20
}

结果前面的字母指出了数组元素的类型. 结果最前面的 [ 表示是一个数组. @ 后面的内容才是真正的地址值, 是一个十六进制的值. 平时习惯性地将整个结果称为地址, 但实际上只有 @ 后面的才是真正的地址. @ 是间隔符号, 是一个固定的格式.

对于字符数组, 打印数组名得到的结果比较特殊:

public static void main(String[] args) {
    char[] char1 = {'1', '2', '3'};
    System.out.println(char1);              // 123
    char[] char2 = {'1', '2', '3', '9'};
    System.out.println(char2);              // 1239
    char[] char3 = {'9', '8', '7'};
    System.out.println(char3);              // 987
}

数组的动态初始化 1

数组的初始化: 在内存中, 为数组容器开辟空间, 并将数据存入容器中的过程.

数组初始化有两种方式: 动态初始化和静态初始化.

动态初始化: 初始化时只指定数组的长度, 由虚拟机为数组分配初始值.

动态初始化的数组的定义:

数据类型 数组名[] = new 数据类型[数组长度];

或者:

数据类型[] 数组名 = new 数据类型[数组长度];  // 两者等价, 但是推荐使用这一种

不同数据类型的数组, 默认的初始值不同.

  • int 类型默认初始值为 0

  • short 类型默认初始值为 0

  • byte 类型默认初始值为 0

  • long 类型默认初始值为 0

  • float 类型默认初始值为 0.0

  • double 类型默认初始值为 0.0

  • char 类型默认初始值为 \u0000 (零字符), 打印出来就是一个空格.

  • boolean 类型默认初始值为 false

  • 引用数据类型默认初始值为 NULL

程序示例:

public class Array {
    public static void main(String[] args) {
        double[] hens = new double[3]; // 定义数组
        int[] kens = new int[4];
        double sumHens = 0;
        int sunKens = 0;
        // 遍历数组
        for (int i = 0; i < hens.length; i++) {
            System.out.print(hens[i] + "\t");
            sumHens += hens[i];
        }
        System.out.println("");
        for (int i = 0; i < kens.length; i++) {
            System.out.print(kens[i] + "\t");
            sunKens += kens[i];
        }
        System.out.println();
        System.out.println(sumHens);
        System.out.println(sunKens);
    }
}

结果:

0.0        0.0        0.0        
0        0        0        0        
0.0
0

程序示例:

public class Array {
    public static void main(String[] args) {
        byte[] bytes = new byte[10];
        short[] shorts = new short[10];
        int[] ints = new int[10];
        long[] longs = new long[10];
        char[] chars = new char[10];  // 字符类型的数组, 默认初始化值为 \u0000, 打印出来就是一个空格
        boolean[] booleans = new boolean[10];
        float[] floats = new float[10];
        double[] doubles = new double[10];
        String[] Strings = new String[10];

        System.out.println(bytes[0]);           // 0
        System.out.println(shorts[0]);          // 0
        System.out.println(ints[0]);            // 0
        System.out.println(longs[0]);           // 0
        System.out.println(chars[0]);
        System.out.println(booleans[0]);        // false
        System.out.println(floats[0]);          // 0.0
        System.out.println(doubles[0]);         // 0.0
        System.out.println(Strings[0]);         // null
    }
}

执行结果:

0
0
0
0
 
false
0.0
0.0
null

程序示例:

// 空字符 \u0000 打印出来就是一个空格
public class Array {
    public static void main(String[] args) {
        System.out.println("Hello\u0000World!");    // Hello World!
    }
}

数组的动态初始化 2

先声明数组, 语法:

数据类型 数组名[];

或者:

数据类型[] 数组名;

再创建数组, 语法:

数组名 = new 数据类型[数组长度];

例如:

int[] a;           // 或者 int a[];   此时还没有在内存中为 a 分配空间, a 是 NULL
a = new int[10];   // 此时在内存中为 a 分配了空间, 只有分配了存储空间, 才能存放数据, 否则报错, 显示空指针异常

其实就是把方式 1 分两步写了.

错误代码:

在未分配空间的情况下, 访问数组的长度:

public class Array {
    public static void main(String[] args) {
        double[] a;
        System.out.println(a.length);  // 可能尚未初始化变量 a
    }
}

程序示例:

public class Test {
    public static void main(String[] args) {
        int[] nums;
        // nums[0] = 1;  // 报错: Variable 'nums' might not have been initialized
        nums = new int[10];
        nums[0] = 1;
        nums[10] = 10;  // 未报错, 但是提示: Array index is out of bounds
        System.out.println(nums[0]);
        // System.out.println(nums[10]);  // 报错: ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 10
    }
}

数组的静态初始化

语法:

完整格式:

数据类型[] 数组名 = new 数据类型[] {元素1, 元素2, ... , 元素n};

例如:

int[] arr1 = new int[]{1, 2, 3};
double[] arr2 = new double[]{1.1, 2.2, 3.3};

简化格式:

数据类型[] 数组名 = {元素1, 元素2, ... , 元素n};

或者:

数据类型 数组名[] = {元素1, 元素2, ... , 元素n};

例如:

int[] a = {1, 2, 3};   // 等价于 int a[] = {1, 2, 3};

程序示例:

// 定义一个数组, 存储 1, 2, 3, 4, 5
// 遍历数组得到每一个元素, 求数组里面所有的数据和
public class Array {
    public static void main(String[] args) {
        int[] nums = {1, 2, 3, 4, 5};
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }
        System.out.println(sum);    // 15
    }
}

程序示例:

// 定义一个数组
// 存储 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
// 遍历数组得到每一个元素, 统计数组里面一共有多少个能被 3 整除的数字

public class Array {
    public static void main(String[] args) {
        int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int count = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] % 3 == 0)
                ++count;
        }
        System.out.println("数组中能被3整除的数字有" + count + "个. ");
    }
}

执行结果:

数组中能被3整除的数字有3个. 

程序示例:

// 定义一个数组, 存储 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
// 遍历数组得到每一个元素. 
// 要求: 
// 1、如果是奇数, 则将当前数字扩大两倍
// 2、如果是偶数, 则将当前数字变成二分之一

public class Array {
    public static void main(String[] args) {
        int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        System.out.println("原始数组为: ");
        for (int i = 0; i < nums.length; i++) {
            System.out.print(nums[i] + "\t");
        }
        // 处理数组
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] % 2 == 0)
                nums[i] /= 2;
            else nums[i] *= 2;
        }
        System.out.println("\n处理之后的数组为: ");
        for (int i = 0; i < nums.length; i++) {
            System.out.print(nums[i] + "\t");
        }
    }
}

执行结果:

原始数组为: 
1        2        3        4        5        6        7        8        9        10        
处理之后的数组为: 
2        1        6        2        10        3        14        4        18        5

程序示例:

// 需求: 已知数组元素为 {33, 5, 22, 44, 55}
// 请找出数组中最大值并打印在控制台

public class Array {
    public static void main(String[] args) {
        int[] nums = {33, 5, 22, 44, 55};
        int max = nums[0];
        for (int i = 0; i < nums.length; i++) {
            max = (max > nums[i] ? max : nums[i]);
        }
        System.out.println("最大值为" + max);
    }
}

执行结果:

最大值为55

程序示例:

// 冒泡排序
public class Test {
    public static void main(String[] args) {
        int[] nums = new int[]{33, 5, 22, 44, 55};
        // i 表示要比较多少趟, 总趟数是数组长度减一
        // 同时, i 也表示两个比较大小的元素其中之一的索引
        for (int i = 0; i < nums.length - 1; i++) {
            // j 表示两个比较大小的元素其中之一的索引, 表示的是 i 元素后面的那个元素
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[i] < nums[j]) {
                    int tmp = nums[i];
                    nums[i] = nums[j];
                    nums[j] = tmp;
                }
            }
        }
        System.out.println("从大到小进行排序:");
        for (int i = 0; i < nums.length; i++) {
            System.out.print(nums[i] + "\t");
        }
    }
}

执行结果:

从大到小进行排序: 
55        44        33        22        5

程序示例:

// 需求: 生成 10 个 1~100 之间的随机数存入数组. 
// 1) 求出所有数据的和
// 2) 求所有数据的平均数
// 3) 统计有多少个数据比平均值小

import java.util.Random;

public class Array {
    public static void main(String[] args) {
        Random r = new Random();

        int[] nums = new int[10];

        // 打印数组初始值
        System.out.println("数组初始值: ");
        for (int i = 0; i < nums.length; i++) {
            System.out.print(nums[i] + "\t");
        }

        // 生成数组的值
        for (int i = 0; i < nums.length; i++) {
            nums[i] = r.nextInt(100) + 1;
        }

        // 打印数组现在的值
        System.out.println("\n数组现在的值: ");
        for (int i = 0; i < nums.length; i++) {
            System.out.print(nums[i] + "\t");
        }

        // 求数组元素之和
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }

        // 求数组元素平均数
        int avg = sum / nums.length;

        // 求数组值小于平均数的个数
        int count = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] < avg)
                ++count;
        }

        System.out.println("\n数组元素之和为" + sum);
        System.out.println("数组元素平均值为" + avg);
        System.out.println("数组中元素小于平均值的个数有" + count + "个");
    }
}

执行结果:

数组初始值: 
0        0        0        0        0        0        0        0        0        0        
数组现在的值: 
72        6        35        98        45        85        16        100        85        47        
数组元素之和为589
数组元素平均值为58
数组中元素小于平均值的个数有5个

程序示例:

// 需求: 定义一个数组, 存入 1, 2, 3, 4, 5. 按照要求交换索引对应的元素. 
// 交换前: 1, 2, 3, 4, 5
// 交换后: 5, 2, 3, 4, 1

public class Array {
    public static void main(String[] args) {
        int[] nums = {1, 2, 3, 4, 5};
        System.out.println("交换之前的数组: ");
        for (int i = 0; i < nums.length; i++) {
            System.out.print(nums[i] + "\t");
        }
        // 交换
        // int i = 0, j = nums.length - 1;
        // while (i <= j) {
        //     int tmp = nums[i];
        //     nums[i] = nums[j];
        //     nums[j] = tmp;
        //     ++i;
        //     --j;
        // }
        for (int i = 0, j = nums.length - 1; i <= j; ++i, --j) {
            int tmp = nums[i];
            nums[i] = nums[j];
            nums[j] = tmp;
        }

        System.out.println("\n交换之后的数组: ");
        for (int k = 0; k < nums.length; k++) {
            System.out.print(nums[k] + "\t");
        }
    }
}

执行结果:

交换之前的数组: 
1        2        3        4        5        
交换之后的数组: 
5        4        3        2        1

程序示例:

// 需求: 定义一个数组, 存入 1~9. 要求随机打乱数组中所有数据的顺序. 

import java.util.Random;

public class Array {
    public static void main(String[] args) {
        int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9};

        System.out.println("初始状态的数组: ");
        for (int i = 0; i < nums.length; i++) {
            System.out.print(nums[i] + "\t");
        }

        Random r = new Random();
        // 每一次遍历到当前数组元素时, 只和后面的随机一个元素进行交换
        for (int i = 0; i < nums.length - 1; i++) {
            int randomIndex = r.nextInt(nums.length - 1 - i - 1 + 1) + i + 1;
            int tmp = nums[i];
            nums[i] = nums[randomIndex];
            nums[randomIndex] = tmp;
        }

        System.out.println("\n方法一打乱之后的数组: ");
        for (int i = 0; i < nums.length; i++) {
            System.out.print(nums[i] + "\t");
        }

        // 每一次遍历到当前数组元素时, 和一个随机元素进行交换
        for (int i = 0; i < nums.length - 1; i++) {
            int randomIndex = r.nextInt(nums.length);
            int tmp = nums[i];
            nums[i] = nums[randomIndex];
            nums[randomIndex] = tmp;
        }

        System.out.println("\n方法二打乱之后的数组: ");
        for (int i = 0; i < nums.length; i++) {
            System.out.print(nums[i] + "\t");
        }
    }
}

执行结果:

初始状态的数组: 
1        2        3        4        5        6        7        8        9        
方法一打乱之后的数组: 
9        8        6        2        4        5        1        7        3        
方法二打乱之后的数组: 
3        1        9        8        7        4        5        6        2

数组动态或者静态初始化时, 允许自动类型转换, 能进行自动类型转换的类型可以被赋值给数组元素, 不能进行自动类型转换的类型不能赋值给数组元素, 否则报错: 不兼容的类型.

程序示例:

public class Array {
    public static void main(String[] args) {
        double[] nums = {1.1, 2.2, 3, 4, 5};    // 此处的静态初始化, 利用了自动类型转换
        for (int i = 0; i < nums.length; i++) {
            System.out.print(nums[i] + "\t");
        }
    }
}

执行结果:

1.1        2.2        3.0        4.0        5.0

IDEA 提供了一个快速生成数组遍历的方式: 数组名.fori


图 1

图 2

数组中的数据类型, 可以是基本类型或者引用类型.

数组下标越界会报错. 编译时不检查是否越界, 运行时才报错.

程序示例:

public class Array {
    public static void main(String[] args) {
        int[] nums = {1, 2, 3};
        System.out.println(nums[3]);  // ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
    }
}

静态初始化中, 我们要手动指定所有的元素, 而不要去指定数组的长度, 数组的长度由系统根据数组的元素来自行计算.

数组赋值

数组赋值是引用传递, 传递的是地址.

程序示例:

public class ArrayAssign {
    public static void main(String[] args) {
        int[] arr1 = {1, 2, 3};
        int[] arr2 = arr1;
        for (int i = 0; i < arr1.length; i++) {
            System.out.print(arr1[i] + " ");
        }
        System.out.println("");
        arr2[1] = 100;
        for (int i = 0; i < arr1.length; i++) {  // 修改 arr2 会影响 arr1
            System.out.print(arr1[i] + " ");
        }
    }
}

执行结果:

1 2 3 
1 100 3 

程序示例:

public class test {
    public static void main(String[] args) {
        // 给定一个初始数组
        int[] arr1 = {1, 2, 3};
        // 遍历打印初始数组
        for (int i = 0; i < arr1.length; i++) {
            System.out.print(arr1[i] + " ");
        }
        // 调用方法修改数组
        change(arr1);
        // 遍历打印修改后的数组
        System.out.println();
        for (int i = 0; i < arr1.length; i++) {
            System.out.print(arr1[i] + " ");
        }
    }

    // 修改数组的方法
    public static void change(int[] arr) {
        arr[0] = 100;
    }
}

执行结果:

1 2 3 
100 2 3 

JVM 内存

JVM 虚拟机在运行时, 会占用一块内存空间. 为了更好地利用这一块内存空间, 虚拟机把这块空间分成了 5 个部分. 每个部分都有其各自的作用.


图 1

在 JDK7 之前, JVM 将方法区和堆放在一起, 在真实的物理内存中, 也是一块连续的空间. 但是这种设计方式不太好 (为什么不好? 有何影响? 需要在虚拟机内存中学习)

从 JDK8 开始, 取消方法区, 新增元空间, 和堆空间分开了. 把原来的方法区的多种功能进行拆分, 有的功能放到了堆中, 有的功能放到了元空间中.

栈: 方法运行时使用的内存, 比如 main 方法运行时会先进栈. 基本数据类型都放在栈中. 方法执行完毕后就会出栈.

寄存器: 给 CPU 使用, 和我们开发无关.

本地方法栈: JVM 在使用操作系统功能的时候使用, 和我们开发无关.

方法区: 存储可以运行的 class 文件. 当一个类要运行时, 要把这个类的字节码文件, 即 class 文件加载到方法区中临时存储.

堆: 存储对象或者数组, 只要是 new 出来的东西, 比如数组, 都存储在堆内存. new 出来的东西会在堆内存中开辟空间并产生地址, 表示在堆内存中的位置.


图 2

方法进栈后, 有一块属于这个方法的空间, 每一个在这个方法中定义的且应该出现在栈内存中的内容都会出现在这个方法的空间中. 比如图 2 中, 变量 a, b 和 c 都是定义在 main() 方法中的, 且这三个变量就应该出现在栈中, 所以这三个变量都出现在 main() 方法的空间中. 最后的 sout(c); 语句也是在 main() 方法中执行的.

数组的静态初始化可以有简化的书写方式, 这种书写方式是没有 new 关键字的, 但是还是认为是有 new 关键字的, 只是省略没写, 所以还是 new 出来的东西.


图 3 数组的内存图

int[] arr = new int[2]; 这一条语句是由左右两个部分组成的. 等号左边是 int[] arr, 是在栈里面定义了一个变量, 变量名为 arr, 类型限定为 int[], 表示变量 arr 可以记录 int 类型数组的地址值. 等号右边 new int[2] 有 new 关键字, 所以是在堆内存中开辟了一块小空间. 堆内存中的内容是有地址值的, 会通过等号即赋值运算符把地址值赋值给左边的变量 arr, 于是变量 arr 也可以通过这个地址值找到堆内存中的这块小空间. 数组 int[] arr 刚开始创建的时候, 两个元素都默认是 0, 所以执行语句 sout(arr[0]);sout(arr[1]); 时输出的都是 0. 执行到语句 arr[0] = 11; 和 语句 arr[1] =22; 时, 将默认的值修改了. 再往下, 执行语句 sout(arr[0]);sout(arr[1]); 时输出的都是修改之后的值了. 执行语句 int[] arr2 = {33, 44, 55}; 时, 直接将元素默认值 0 改为了指定的值.

在栈内存中的基本类型的变量存储的都是真实的值, 而引用类型如数组名, 存储的是堆内存中的地址值. 所以在用 sout 打印数组名这个变量时, 输出的是地址值.


图 4 两个数组指向同一个空间的内存图

arr1 和 arr2 指向了堆内存中的同一块小空间.


图 5 两个数组指向同一个空间的内存图

图 6 两个数组指向同一个空间的内存图

图 7 数组的内存图

int[] arr1 中的 int[] 表示 arr1 可以存放 int 型数组的地址.

当两个数组指向堆内存中的同一个小空间时, 如果其中一个数组改变小空间中的值, 那么其他数组再次访问该堆内存时, 得到的就是修改之后的值了.

拷贝数组

要求: 将数组 arr1 拷贝到数组 arr2. 进行值传递, 而不是引用传递, 即拷贝之后的数组的改变, 不影响原数组, 两个数组的内存空间是独立的.

思路:

  1. 新建一个数组 arr2, 长度等于 arr1.

  2. 利用 for 循环遍历 arr1 的所有元素, 将其复制到 arr2 中.

程序示例:

public class ArrayCopy {
    public static void main(String[] args) {
        int[] arr1 = { 1, 2, 3 }; // 定义数组 arr1
        int[] arr2 = new int[arr1.length]; // 定义新数组 arr2
        // 遍历数组 arr1, 将每一个元素值复制给 arr2 的对应位置
        for (int i = 0; i < arr1.length; i++) {
            arr2[i] = arr1[i];
        }
        // 输出 arr1
        System.out.print("arr1:");
        for (int i = 0; i < arr1.length; i++)
            System.out.print(arr1[i] + " ");
        System.out.println("");
        // 输出 arr2
        System.out.print("arr2:");
        for (int i = 0; i < arr1.length; i++)
            System.out.print(arr2[i] + " ");
        System.out.println("");
        // 修改 arr2 的某一个元素
        arr2[1] = 999;
        // 输出 arr1
        System.out.print("arr1:");
        for (int i = 0; i < arr1.length; i++)
            System.out.print(arr1[i] + " ");
        System.out.println("");
        // 输出修改后的 arr2
        System.out.print("arr2:");
        for (int i = 0; i < arr1.length; i++)
            System.out.print(arr2[i] + " ");
        System.out.println("");
    }
}

结果:

arr1:1 2 3 
arr2:1 2 3
arr1:1 2 3
arr2:1 999 3

二维数组

初始化的两种方法: 静态初始化和动态初始化.

静态初始化

public static void main(String[] args) {
    int[][] arr1 = new int[][]{{11, 22, 33, 44}, {33, 44, 55, 66}};
    System.out.println(arr1.length);        // 2
    int[][] arr2 = new int[][]{{11, 22, 33}, {44, 55, 66}, {44, 55, 66}, {44, 55, 66}};
    System.out.println(arr2.length);        // 4, 说明对于二维数组而言, arr.length 表示一维数组的个数
}
public static void main(String[] args) {
    int arr3[][] = {                        // 建议将多个一维数组分行书写, 每行一个一维数组
            {11, 22, 33},
            {44, 55, 66},
            {44, 55, 66},
            {44, 55, 66},
            {11, 22, 33}
    };
    System.out.println(arr3.length);        // 5
    System.out.println(arr3[0]);            // [I@119d7047
    System.out.println(arr3[1]);            // [I@776ec8df
    System.out.println(arr3[2]);            // [I@4eec7777
}
public static void main(String[] args) {
    int[][] arr4 = {{1, 2, 3}, {}, {4, 5}}; // 简化写法, 且子数组元素个数不同
    System.out.println(arr4.length);        // 3
    System.out.println(arr4[0].length);     // 3
    System.out.println(arr4[1].length);     // 0
    System.out.println(arr4[2].length);     // 2
    // for (int i = 0; i < arr4[0].length; i++) {
    //     for (int j = 0; j < arr4[1].length; j++) {
    //         System.out.println(arr4[i][j]); // Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 2
    //     }
    // }
    // 正确的打印二维数组的方法: 
    for (int i = 0; i < arr4.length; i++) {
        for (int j = 0; j < arr4[i].length; j++) {
            System.out.print(arr4[i][j] + "  ");
        }
        System.out.println();
    }
    System.out.println(arr4[0][2]);         // 3
}

执行结果:

1  2  3  

4  5  
3

动态初始化

格式:

数据类型[][] 数组名 = new 数据类型[m][n];
m 表示这个二维数组可以存放多少个一维数组.
n 表示每一个一维数组可以存放多少个元素.

范例:

int[][] arr = new int[2][3];    // 该数组可以放 2 个一维数组, 每一个一维数组可以放 3 个 int 类型的元素.
public static void main(String[] args) {
    int[][] arr1 = new int[2][3];
    arr1[1][2] = 10;
    for (int i = 0; i < arr1.length; i++) {
        for (int j = 0; j < arr1[i].length; j++) {
            System.out.print(arr1[i][j] + "  ");
        }
        System.out.println();
    }
}

执行结果:

0  0  0  
0  0  10

没有初始化的地方是默认初始化, 因此用这一种动态初始化方式定义的数组, 一定是四四方方的, 不会像静态初始化那样有空缺的角.

二维数组的内存图

public static void main(String[] args) {
    int[][] arr = new int[2][3];
}

arr 是变量名.

int[][] 是类型, 表示 arr 可以记录 int 类型的二维数组的地址值.


图 1

图 2

需要手动创建一维数组.


图 3

图 4

程序示例:

public class test {
    public static void main(String[] args) {
        // 动态定义一个二维数组
        int[][] arr = new int[2][3];

        // 打印数组的初始值
        print(arr);

        System.out.println("-------------");

        // 定义两个一维数组, 长度和二维数组中的一维数组长度刚好相等
        int[] arr1 = {1, 2, 3};
        int[] arr2 = {4, 5, 6};

        // 将两个一维数组关联到二维数组中
        arr[0] = arr1;
        arr[1] = arr2;

        // 打印修改后的二维数组
        print(arr);

        System.out.println("-------------");

        // 定义两个一维数组, 长度分别比二维数组中的一维数组长度短和长
        int[] arr3 = {1, 2};
        int[] arr4 = {3, 4, 5, 6};

        // 将两个一维数组关联到二维数组中
        arr[0] = arr3;
        arr[1] = arr4;

        // 打印修改后的二维数组
        print(arr);
    }

    // 打印二维数组
    private static void print(int[][] arr) {
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[i].length; j++) {
                System.out.print(arr[i][j] + " ");
            }
            System.out.println();
        }
    }
}

执行结果:

0 0 0 
0 0 0 
-------------
1 2 3 
4 5 6 
-------------
1 2 
3 4 5 6 

二维数组的练习题:

// 某商城每个季度的营业额如下: 单位 (万元)
//         第一季度: 22, 66, 44
//         第二季度: 77, 33, 88
//         第三季度: 25, 45, 65
//         第四季度: 11, 66, 99
//         要求计算出每个季度的总营业额和全年的总营业额

public class TwoDi {
    public static void main(String[] args) {
        int[][] arr = new int[][]{
                {22, 66, 44},
                {77, 33, 88},
                {25, 45, 65},
                {11, 66, 99}
        };
        int sum = 0;
        int[] quarters = new int[4];
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[i].length; j++) {
                quarters[i] += arr[i][j];
                sum += arr[i][j];
            }
        }
        System.out.println("各个季度的营业额: ");
        for (int i = 0; i < quarters.length; i++) {
            System.out.print(quarters[i] + "  ");
        }
        System.out.println();
        System.out.println("全年的营业额: ");
        System.out.println(sum);
    }
}

执行结果:

各个季度的营业额: 
132  198  135  176  
全年的营业额: 
641

另一种写法:

// 某商城每个季度的营业额如下: 单位万元
//         第一季度: 22, 66, 44
//         第二季度: 77, 33, 88
//         第三季度: 25, 45, 65
//         第四季度: 11, 66, 99
//         要求计算出每个季度的总营业额和全年的总营业额

public class TwoDi {
    public static void main(String[] args) {
        int[][] arr = new int[][]{
                {22, 66, 44},
                {77, 33, 88},
                {25, 45, 65},
                {11, 66, 99}
        };

        System.out.println("各个季度的营业额: ");
        for (int i = 0; i < arr.length; i++) {
            System.out.println("第 " + (i + 1) + " 季度: " + getSumArr(arr[i]));
        }
        System.out.println("全年的营业额: " + getSumArrArr(arr));

    }

    public static int getSumArr(int[] arr) {
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        return sum;
    }

    public static int getSumArrArr(int[][] arr) {
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[i].length; j++) {
                sum += arr[i][j];
            }
        }
        return sum;
    }
}

执行结果:

各个季度的营业额: 
第 1 季度: 132
第 2 季度: 198
第 3 季度: 135
第 4 季度: 176
全年的营业额: 641

优化之后:

// 某商城每个季度的营业额如下: 单位万元
//         第一季度: 22,66,44
//         第二季度: 77,33,88
//         第三季度: 25,45,65
//         第四季度: 11,66,99
//         要求计算出每个季度的总营业额和全年的总营业额

public class TwoDi {
    public static void main(String[] args) {
        int[][] arr = new int[][]{
                {22, 66, 44},
                {77, 33, 88},
                {25, 45, 65},
                {11, 66, 99}
        };
        int yearSum = 0;
        System.out.println("各个季度的营业额: ");
        for (int i = 0; i < arr.length; i++) {
            System.out.println("第 " + (i + 1) + " 季度: " + getSumArr(arr[i]));
            yearSum = yearSum + getSumArr(arr[i]);
        }
        System.out.println("全年的营业额: " + yearSum);
    }

    public static int getSumArr(int[] arr) {
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        return sum;
    }
}
posted @ 2024-08-31 22:50  有空  阅读(19)  评论(0)    收藏  举报