七、数组

232 阅读7分钟

一、数组介绍

1. 什么是数组?

数组指的是一种容器,可以用来存储同种数据类型的多个值

  • 数组容器在存储数据的时候,需要结合隐式转换考虑

    • 例如:int 类型的数组容器可以存(byte,short,int)
    • 例如:double类型的数组容器可以存(byte,short,int,long,float,double)
    • 建议:容器的类型,和存储的数据类型保持一致

二、数组的定义与静态初始化

1. 数组的定义

格式一

数据类型[] 数组名

格式二

数据类型 数组名[]

2. 数组的静态初始化

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

完整格式: 数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3...};

范例:int[] arr = new int[]{1,2,3};

简化格式:数据类型[] 数组名 = {元素1,元素2,元素3...};

范例:int[] arr = {1,2,3};

public class ArrayDefine {  
    public static void main(String[] args) {  
        int[] arr1 = new int[]{1,2,3,4};  
        int[] arr2 = {1,2,3,4};  

        String[] arr3 = new String[]{"张三", "李四", "王五"};  
        String[] arr4 = {"张三", "李四", "王五"};  

        double[] arr5 = new double[]{1.1,1.2,1.3};  
        double[] arr6 = {1.1,1.2,1.3};  
    }  
}

3. 数组的地址值

int[] arr = new int[]{1,2,3,4,5};
System.out.println(arr); // [I@6d03e736 地址值

数组的地址值表示数组在内存中的位置

解释地址值的格式含义 [I@6d03e736

[:表示当前是一个数组

I:表示当前数组里面的元素都是 int 类型的

@:表示一个间隔符号(固定格式)

[I@6d03e736:才是数组真正的地址值(十六进制)

平时习惯将这个整体称为数组的地址值

三、数组元素访问

1. 数组元素访问

格式:数组名[索引];

2. 索引

索引:也叫下标,角标;

索引的特点:从0开始,逐个+1增长,连续不间断

public class ArrTest3 {  
    public static void main(String[] args) {  
        int[] arr = {1,2,3,4,5};  
        // 数组长度:数组的 length 属性  
        for(int i = 0; i < arr.length; i++) {  
            System.out.println(arr[i]);  
        }  
    }  
}

四、数组遍历

1. 数组遍历

数组遍历:将数组中所有的内容取出来,取出来之后可以(打印、求和、判断...)

注意:遍历指的是取出数据的过程,不要局限的理解为,遍历就是打印

public class ArrTest3 {  
    public static void main(String[] args) {  
        int[] arr = {1,2,3,4,5};  
        // 数组长度:数组的 length 属性  
        for(int i = 0; i < arr.length; i++) {  
            System.out.println(arr[i]);  
        }  
    }  
}

练习:遍历数组并求和

定义一个数组,存储1,2,3,4,5

遍历数组得到每一个元素,求数组里面所有的数据和

public class ArrTest6 {  
    public static void main(String[] args) {  
        int[] arr = new int[]{1,2,3,4,5,6,7,8,9,10};  
        for (int i = 0; i < arr.length; i++) {  
            if (arr[i] % 2 == 0) {  
                arr[i] = arr[i] / 2;  
            } else {  
                arr[i] = arr[i] * 2;  
            }  
        }  
        for (int i = 0; i < arr.length; i++) {  
            System.out.println(arr[i]);  
        }  
    }  
}

练习:统计个数

定义一个数组,存储1,2,3,4,5,6,7,8,9,10

遍历数组得到每一个元素,统计数组里面一共有多少个能被 3 整除的数字

public class ArrTest6 {  
    public static void main(String[] args) {  
        int[] arr = new int[]{1,2,3,4,5,6,7,8,9,10};  
        for (int i = 0; i < arr.length; i++) {  
            if (arr[i] % 2 == 0) {  
                arr[i] = arr[i] / 2;  
            } else {  
                arr[i] = arr[i] * 2;  
            }  
        }  
        for (int i = 0; i < arr.length; i++) {  
            System.out.println(arr[i]);  
        }  
    }  
}

练习:变化数据

定义一个数组,存储1,2,3,4,5,6,7,8,9,10

遍历数组得到每一个元素

要求:

  1. 如果是奇数,则将当前数字扩大两倍

  2. 如果是偶数,则将当前数字变成二分之一

public class ArrTest6 {  
    public static void main(String[] args) {  
        int[] arr = new int[]{1,2,3,4,5,6,7,8,9,10};  
        for (int i = 0; i < arr.length; i++) {  
            if (arr[i] % 2 == 0) {  
                arr[i] = arr[i] / 2;  
            } else {  
                arr[i] = arr[i] * 2;  
            }  
        }  
        for (int i = 0; i < arr.length; i++) {  
            System.out.println(arr[i]);  
        }  
    }  
}

五、数组动态初始化

1. 为什么有数组动态初始化呢

  • int[] arr = {1,2,3,4,5};
  • int[] arr = {???};

2. 数组动态初始化

动态初始化:初始化时,只指定数组长度,由系统为数组分配初始值

  • 格式:数据类型[] 数组名 = new 数据类型[数组长度];
  • 范例:int[] arr = new int[3];
public class AttTest7 {  
    public static void main(String[] args) {  
        String[] arr = new String[50];  
        arr[0] = "张三";  
        arr[1] = "李四";  

        System.out.println(arr[0]); // 张三  
        System.out.println(arr[1]); // 李四  
        System.out.println(arr[2]); // 打印出来的是默认初始化值 null  

        int[] arr2 = new int[3];  
        System.out.println(arr2[0]); // 0  
        System.out.println(arr2[1]); // 0  
        System.out.println(arr2[2]); // 0  
    }  
}

3. 数组动态初始化和静态初始化的区别

  • 动态初始化:手动指定数组长度,由系统给出默认初始化值
    • 只明确元素个数,不明确具体数值,推荐使用动态初始化
      • 举例:使用数组容器来存储键盘录入的5个整数
      • int[] arr = {?????};
      • int[] arr = new int[5];
  • 静态初始化:手动指定数组元素,系统会根据元素个数,计算出数组的长度
    • 需求中已经明确了要操作的具体数据,直接静态初始化即可
      • 举例:将全班的学生成绩存入数组中 11,22,33
      • int[] arr = {11,22,33};

4. 数组默认初始化值的规律

  • 整数类型:默认初始化值 0
  • 小数类型:默认初始化值 0.0
  • 字符类型:默认初始化值 '/u0000' 空格
  • 布尔类型:默认初始化值 false
  • 引用数据类型:默认初始化值 null

六、数组内存图

1. Java内存分配

  • 栈:方法运行时使用的内存,比如 main 方法运行,进入方法栈中执行
  • 堆:存储对象或者数组,new 来创建的,都存储在堆内存
  • 方法区:存储可以运行的 class 文件
  • 本地方法栈:JVM 在使用操作系统功能的时候使用,和开发无关
  • 寄存器:给 CPU 使用,和开发无关

堆栈.png

java内存.png

2. 数组存储

    1. 只要是 new 出来的一定是在 堆 里面开辟了小空间
    1. 如果 new 了很多次,那么在 堆 里面有很多小空间,每个小空间中都有各自的数据

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

    1. 当两个数组指向同一个小空间时,其中一个数组对小空间中的值发生了改变,那么其它数组再次访问的时候都是修改之后的结果了

七、数组常见问题

1. 数组常见问题

  • 当访问了数组中不存在的索引,就会引发索引越界异常
  • 索引范围
    • 最小索引:0
    • 最大索引:(数组长度 - 1)
public class ArrTest8 {  
    public static void main(String[] args) {  
        int[] arr = {1,2,3,4,5};  
        // Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5 at com.hrwar.test.ArrTest8.main(ArrTest8.java:6) 
        System.out.println(arr[5]);  
    }  
}

八、数组常见操作

1. 求最值

需求:已知数组元素为{33,5,22,44,55}

请找出数组中最大值并打印在控制台

public class ArrTest9 {  
    public static void main(String[] args) { 
        int[] arr = {33,5,22,44,55};  
        int max = arr[0];  
        for (int i = 0; i < arr.length; i++) {  
            if (arr[i] > max) {  
                max = arr[i];  
            }  
        }  
        System.out.println(max);  
    }  
}
  • 扩展
      1. 根据求最大值的思路,改写一个最小值
      1. 为什么max要记录为arr[0],默认不能为 0 吗?
      • 不能,可能是全 负 数组
      1. 循环中开始条件一定是 0 吗?
      • 不一定,可自定义

2. 求和

需求:生成10个1~100之间的随机数存入数组

  1. 求出所有数据的和

  2. 求所有数据的平均数

  3. 统计有多少个数据比平均值小

import java.util.Random;  

public class ArrTest10 {  
    public static void main(String[] args) {  
        int[] arr = new int[10];  
        Random r = new Random();  
        for (int i = 0; i < arr.length; i++) {  
            int number = r.nextInt(100) + 1;  
            arr[i] = number;  
        }  

        int sum = 0;  
        for (int i = 0; i < arr.length; i++) {  
            sum += arr[i];  
        }  
        System.out.println("数组中所有数据的和为:" + sum);  

        int avg = sum / arr.length;  
        System.out.println("数组中平均数为:" + avg);  

        int count = 0;  
        for (int i = 0; i < arr.length; i++) {  
            if (arr[i] < avg) {  
            count++;  
            }  
        }  
        System.out.println("在数组中,一共有" + count + "个数据,比平均数小");  

        // 遍历数组,验证答案  
        for (int i = 0; i < arr.length; i++) {  
            System.out.print(arr[i] + " ");  
        }  
    }  
}

3. 交换数据

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

交换前:1,2,3,4,5

交换后:5,4,3,2,1

public class ArrTest11 {  
    public static void main(String[] args) {  
        int[] arr = {1,2,3,4,5};  
        for (int i = 0, j = arr.length - 1; i < j; i++, j--) {  
            int temp = arr[i];  
            arr[i] = arr[j];  
            arr[j] = temp;  
        }  

        for (int i = 0; i < arr.length; i++) {  
            System.out.print(arr[i] + " ");  
        }  
    }  
}

4. 打乱数据

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

import java.util.Random;  
public class ArrTest12 {  
    public static void main(String[] args) {  
        int[] arr = {1,2,3,4,5};  
        Random r = new Random();  
        for (int i = 0; i < arr.length; i++) {  
            int randomIndex = r.nextInt(arr.length);  
            int temp = arr[i];  
            arr[i] = arr[randomIndex];  
            arr[randomIndex] = temp;  
        }  

        for (int i = 0; i < arr.length; i++) {  
            System.out.print(arr[i] + " ");  
        }  
    }  
}