Java数组详解

  • Post author:
  • Post category:java




一、数组的介绍

1、数组是相同类型数据的有序集合。

2、数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。

3、其中每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们。



二、数组的声明和创建

首先必须声明数组变量,才能在程序中使用数组。下面是数组定义格式:

数据类型[ ] 数组名; --> int[] array; //首选的方法
或
数据类型 数组名[ ]; --> int array[]; //不是首选的方法,但效果相同

数组元素是通过索引访问的,

访问格式为:数组名 [索引]




索引

:数组容器中空间所对应的编号,编号从0开始,逐个 +1 增长。

在数组中有快速

获取数组长度

的方法,

格式为:数组名.length



三、数组的初始化

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



1. 静态初始化

完整格式为:

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


简化格式为:

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


例如:

int[] array1 = new int[]{1,2,3,4,5,6,7,8,9};
int[] array2 = {1,2,3,4,5,6,7,8,9};



2. 动态初始化

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

格式:

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


例如:

int[] array = new int[10];


分配默认值的分类



整数:0

小数:0.0

布尔:false

字符:‘\u0000’ –> Unicode字符 –> 常见体现的是空白字符

字符串:null(其实给引用数据类型分配的默认值都为null)


注意:引用数据类型包括数组、类和接口(这边的字符串String其实也是类)



3. 两种初始化的区别

动态初始化:手动指定

数组长度

,由系统给出默认初始化值。

静态初始化:手动指定

数组元素

,系统会根据元素个数,计算出数组的长度。



4. 两种初始化的使用选择

静态初始化:如果要操作的数据需求中已经明确给出了,那么直接静态初始化。

例如,已知班级学生成绩为100、90、80、60、55,求最高分数:

public static void getMax(){
        int[] score = {100,90,80,60,55};
        int max = 0;
        for (int i = 0; i < score.length; i++) {
            if(score[i] > max){
                max = score[i];
            }
        }
        System.out.println("最高分数为:" + max);
    }

动态初始化:只明确元素个数,不明确具体数值。

例如1,键盘录入5个学生成绩后,找出最高分:

public static void scannerGetMax(){
        Scanner sc = new Scanner(System.in);
        //动态初始化数组
        int[] score = new int[5];
        System.out.println("请输入学生的成绩:");
        for (int i = 0; i < score.length; i++) {
            System.out.println("这是第" + (i + 1) + "个学生的成绩:");
            //录入学生成绩
            score[i] = sc.nextInt();
        }
        //寻找最高分数
        int max = 0;
        for (int i = 0; i < score.length; i++) {
            if(score[i] > max){
                max = score[i];
            }
        }
        System.out.println("最高分数为:" + max);
    }

运行结果为:

请输入学生的成绩:
这是第1个学生的成绩:
90
这是第2个学生的成绩:
60
这是第3个学生的成绩:
70
这是第4个学生的成绩:
95
这是第5个学生的成绩:
85
最高分数为:95

例如2,产生10个1~100之间的随机数,找出最小值:

public static void randomGetMin(){
        Random r = new Random();
        //动态初始化数组
        int[] randomNumber = new int[10];
        for (int i = 0; i < randomNumber.length; i++) {
            //产生的随机数用数组储存
            randomNumber[i] = r.nextInt(100)+1;
            System.out.println("第" + (i + 1) + "个随机数为:" + randomNumber[i]);
        }
        //寻找最小值
        int min = randomNumber[0];
        for (int i = 1; i < randomNumber.length; i++) {
            if(randomNumber[i] < min){
                min = randomNumber[i];
            }
        }
        System.out.println("产生的最小随机数为:" + min);
    }

运行结果为:

1个随机数为:512个随机数为:363个随机数为:674个随机数为:615个随机数为:766个随机数为:247个随机数为:218个随机数为:889个随机数为:3010个随机数为:47
产生的最小随机数为:21



四、数组的遍历

其实在上述两种数组初始化的使用选择中已经涉及到数组的遍历,这边通过一个实例来系统总结。

已知,学生成绩为100、50、20、90、85。

计算出学生成绩的平均值,并计算出成绩低于平均值的学生个数,打印在控制台上:

public static void getAvg(){
        int[] score = {100,50,20,90,85};
        int sum = 0;
        //数组遍历的应用:数组元素求和
        for (int i = 0; i < score.length; i++) {
            sum = sum + score[i];
        }
        //注意:sum和学生个数都为整数,两个整数相除会自动省去小数部分,所以此处 sum*1.0 是为了保留小数
        double avg = (sum * 1.0) / score.length;
        System.out.println("成绩的平均分为:" + avg);
        int count = 0;
        //数组遍历的应用
        for (int i = 0; i < score.length; i++) {
            if(score[i] <avg){count++;}
        }
        System.out.println("成绩低于平均分的学生个数为:" + count);
    }

运行结果为:

成绩的平均分为:69.0
成绩低于平均分的学生个数为:2


数组遍历快捷键:数组名.fori



五、数组内存图



1. Java内存分配介绍

Java内存分为堆、栈、方法区。




堆可以存放 new 的对象和数组;堆可以被所有的线程共享,不会存放别的对象引用。




方法运行时所进入的内存;栈存放

基本变量类型

(会包含这个基本类型的具体数值)和

引用对象的变量

存放这个引用在堆里面的具体地址)。



方法区

字节码文件加载时进入的内存;方法区可以被所有的线程共享;方法区包含了所有的 class 和 static 变量。


一个数组在内存中的操作过程:


在这里插入图片描述

int[ ] arr = {11,22,33};的完整格式为 int[ ] arr = new int[ ]{11,22,33};堆内存中存放了数组的元素,并将数组的地址交给栈内存中的 arr 变量,那么 arr 变量就可以通过该地址找到该空间,然后对数组元素重新赋值,最后在控制台打印数组元素。


两个数组指向相同内存图:


在这里插入图片描述

同理,通过 new 产生的数组 array1 存放在堆内存中,并将数组的地址交给栈内存中的 array1 变量,那么 array1 变量就可以通过该地址找到该空间,随后将数组 array1 的地址复制一份交给了变量 array2,这样 array2 变量也可以通过该地址找到该 array1 数组,并对其 array1[0]重新赋值。



2. 方法参数传递

例如:

public class ArgsTest1 {
    public static void main(String[] args) {
        int number = 100;
        System.out.println("调用change方法前:" + number);
        change(number);
        System.out.println("调用change方法后:" + number);
    }
    public static void change(int number){
        number = 200;
    }
}

运行结果为:

调用change方法前:100
调用change方法后:100

在内存中如下图所示:

在这里插入图片描述

主方法进入栈内存,给 number 赋值100,打印输出语句,此时调用 change 方法,change 方法进入栈内存,一开始接收到的形参 number 的值为100,后来 change 方法修改了 number 的值为200,但是这与主方法里面的 number 的值无关,主方法里面的 number 的值依然是100,然后 change 方法调用结束,弹出栈内存:

在这里插入图片描述

此时主方法里面 number 的值仍然为100,所以打印输出语句时 number 的值为100。

但是如果一定要让 change 方法里面修改值的操作生效,可以在 change 方法里面返回 number 的值:

public class ArgsTest1_1 {
    public static void main(String[] args) {
        int number = 100;
        System.out.println("调用change方法前:" + number);
        number = change(number);
        System.out.println("调用change方法后:" + number);
    }
    public static int change(int number){
        number = 200;
        return number;
    }
}

运行结果为:

调用change方法前:100
调用change方法后:200

再看下面一个有关

数组

的方法参数传递的例子:

public class ArgsTest2 {
    public static void main(String[] args) {
        int[] arr = {11,22,33,44,55};
        System.out.println("调用change方法前:" + arr[0]);
        change(arr);
        System.out.println("调用change方法后:" + arr[0]);
    }
    public static void change(int arr[]){
        arr[0] = 66;
    }
}

运行结果为:

调用change方法前:11
调用change方法后:66

在内存中如下图所示:

在这里插入图片描述

首先主方法进入栈内存,在主方法中通过 new 创建的数组 arr 存放在堆内存中,通过元素个数开辟空间存放,随后将数组地址交给栈内存中的 arr 变量,arr 变量可以通过该地址找到数组,接着打印输出语句,然后调用 change 方法,change 方法进入栈内存,通过形参的传递 change 方法里面也有一个 arr 变量,但是

注意这与主方法中的 arr 变量不是一回事

(好比于不同班级中同名的学生),通过传递,change 方法里面的 arr 变量也可以得到主方法中 arr 变量的地址,那么 change 方法里面的 arr 变量也可以通过地址寻找到堆内存中对应的数组,所以 change 方法里面的对数组的修改就可以生效。当 change 方法调用结束,change 方法弹出栈内存:

在这里插入图片描述

但是此时堆内存中的数组已经被修改了,所以再次打印输出语句时打印的是修改之后的数组内容。

最后,对于方法参数传递问题,做一个总结:


对于基本数据类型,传递的是数据值;

对于引用数据类型,传递的是地址值。

Java之父的回答:值传递,因为地址值也是值。



六、数组中两个常见问题



1. 索引越界异常

ArrayIndexOutOfBoundsException:当访问了数组中不存在的索引,就会引发索引越界异常。

例如:

public class ArrayIndexOutOfBoundsExceptionDemo {
    public static void main(String[] args) {
        //数组越界
        int[] arr = new int[10];
        System.out.println(arr[10]);//报错
    }
}



2. 空指针异常

NullPointerException:当引用数据类型的变量被赋值为 null 之后,就代表跟堆内存的连接被切断了,如果这时候还想去访问堆内存的数据,就会出现空指针异常。

例如:

public class NullPointerExceptionDemo {
    public static void main(String[] args) {
    	//空指针异常
        int[] arr = {11,2233};
        arr = null;
        System.out.println(arr[0]);//报错
    }
}



七、二维数组的介绍

二维数组是一种容器,该容器用于存储一维数组。

使用思路:若要操作

多组

数据,这些数据属于同一个大类,就可以考虑使用二维数组进行维护。



八、二维数组的初始化



1. 静态初始化

完整格式为:

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


简化格式为:

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


例如:

int[][] array1 = new int[][]{{1,2,3},{4,5,6}};
int[] array2 = {{1,2,3},{4,5,6}};


注意:二维数组在存储一维数组的时候,具体存储的是一维数组的地址值。


例如:

public class ArrayDemo01 {
    public static void main(String[] args) {
        int[][] arr = {
                {11,22,33},
                {44,55,66}
        };
        System.out.println(arr);    //[[I@1b6d3586
        System.out.println(arr[0]); //[I@4554617c
        System.out.println(arr[1]); //[I@74a14482
    }
}


二维数组的元素访问格式:数组名:[m索引][n索引]


其中m索引指定访问哪一个一维数组,n索引指定访问一维数组中的哪一个元素。



2. 二维数组的遍历

思路:

a. 遍历二维数组,获取到每一个一维数组。

b. 继续遍历一维数组,获取到具体的元素。

以下是二维数组的遍历代码:

public static void printArray(int[][] arr){
        //遍历二维数组,获取到每一个一维数组arr[i]
        for (int i = 0; i < arr.length; i++) {
            //继续遍历获取到的一维数组arr[i]
            for (int j = 0; j < arr[i].length; j++) {
                //打印输出
                System.out.println(arr[i][j]);
            }
        }
    }

以下是遍历二维数组并求和代码:

  public static int getSum(int[][] arr){
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[i].length; j++) {
                sum = sum + arr[i][j];
            }
        }
        System.out.println("求和为:" + sum);
        return sum;
    }



3. 动态初始化

格式:

数据类型[ ][ ] 数组名 = new 数据类型[m][n];


其中,m 表示这个二维数组可以存放多少个一维数组,n 表示每一个一维数组可以存放多少个元素。

例如:

int[][] array = new int[2][3];//该二维数组可以存放2个一维数组,每个一维数组可以存放3个元素

系统也会为二维数组分配默认值,其分配原则与一维数组一致。

另外,

可以将提前创建好的一维数组,直接存放入二维数组中

public static void main(String[] args) {
        int[] arr1 = {11,22,33};
        int[] arr2 = {44,55,66};
        int[][] arr = new int[2][3];
        arr[0] = arr1;
        arr[1] = arr2;
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[i].length; j++) {
                System.out.println(arr[i][j]);
            }
        }
    }

运行结果为:

11
22
33
44
55
66

下面我们通过内存图来更好理解这个过程:

在这里插入图片描述

首先主方法进入栈内存,接着通过 new 创建的二维数组 arr 存放在堆内存中,因为数组属于引用数据类型,所以系统给该二维数组分配的默认值为 null,但是该二维数组可以存放2个一维数组,每个一维数组可以存放3个元素,所以又要开辟两个一维数组的空间来存放3个元素,然后将每个一维数组的地址存放在二维数组中,最后二维数组才将它的地址交给栈内存中的 arr 变量。

在这里插入图片描述

接着执行下面的语句,又通过 new 创建了两个一维数组 arr1 和 arr2,存放在堆内存中,然后分别将各自数组地址交给栈内存中的 arr1 变量和 arr2 变量。接着继续执行下面的语句,arr 变量通过地址找寻到二维数组中存放第1个一维数组的地址值,然后将 arr1 变量的地址进行地址的替换,这样下次再次寻找该二维数组中的一维数组时,就会找到一维数组 arr1。同理,二维数组中存放的第2个一维数组也是如此的操作过程。



九、Arrays类



1. Arrays类介绍

a、此类包含用来操作数组(比如排序和搜索)的各种方法。此类还包含一个允许将数组作为列表来查看的静态工厂。

b、除非特别注明,否则如果指定数组引用为 null,则此类中的方法都会抛出 NullPointerException。



2. Arrays类中的静态方法

介绍较为常用的三种静态方法。



toString方法

public class arrayDemo06 {
    public static void main(String[] args) {
        int[] array = {1,5,2,6,8,9,4,3,10};
        //toString方法用来打印数组的内容
        //将数组按照默认格式输出为字符串
        System.out.println(Arrays.toString(array));
    }
}
//结果为:[1, 5, 2, 6, 8, 9, 4, 3, 10]



sort方法

public class arrayDemo06 {
    public static void main(String[] args) {
        int[] array = {1,5,2,6,8,9,4,3,10};
        //sort方法用来对数组内容进行升序排序
        Arrays.sort(array);
        System.out.println(Arrays.toString(array));
    }
}
//结果为:[1, 2, 3, 4, 5, 6, 8, 9, 10]


注意

:参数array如果是

数值

默认升序排序;如果是

字符串(英文)

按照字母顺序升序排序;如果是

字符串(中文)

按照字符串的编码数字升序排序;如果是

自定义类型

,那么自定义的类必须有comparable或者Comparator接口的支持。



binarySearch方法

public class arrayDemo06 {
    public static void main(String[] args) {
        int[] array = {1,5,2,6,8,9,4,3,10};
        //toString方法用来打印数组的内容
        System.out.println(Arrays.toString(array));
        //sort方法用来对数组内容进行升序排序
        Arrays.sort(array);
        System.out.println(Arrays.toString(array));
        //使用二分法查找元素在数组中的索引位置
        int num = Arrays.binarySearch(array,5);
        System.out.println("数字5在数组中的索引为"+num);
    }
}
//结果为:数字5在数组中的索引为4


注意

:使用二分搜索法来搜索指定数组,以获得指定的值。必须在进行此调用之前对数组进行排序(通过 sort 方法)。如果没有对数组进行排序,则结果是不确定的。如果数组包含多个带有指定值的元素,则无法保证找到的是哪一个。



版权声明:本文为weixin_51980361原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。