Hello World!|

-Miao酱-

园龄:9个月粉丝:0关注:4

Java 数组

1. 何为数组(Array)

定义:
数组是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,通过数组下标或索引的方式对这些数据进行统一管理。例如全班同学的数学成绩就可以构成一个数组。

array

特点:

  • 数组属于引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。
  • 数组中的元素在内存中是依次紧密排列的,有序的。
  • 数组的下标从0开始,范围是[0, 数组的长度-1],可以通过数组下标的方式访问指定位置的元素。
  • 数组会开辟内存中的一整块连续空间。占据的空间的大小,取决于数组的长度和数组中元素的类型。
  • 数组名中引用的是这块连续空间的首地址
  • 数组一旦初始化完成,其长度就是确定的。数组的长度一旦确定,就不能修改。

分类:

(1)按照元素类型分:

  • 基本数据类型元素的数组:每个元素位置存储基本数据类型的值
  • 引用数据类型元素的数组:每个元素位置存储对象的首地址

(2)按照维度分:

  • 一维数组:存储一组数据
  • 二维数组:存储多组数据,相当于二维表,一行代表一组数据,只是这里的二维表每一行长度不要求一样。

2. 一维数组

2.1 一维数组的声明

语法格式:

    //推荐
    元素的数据类型[] 一维数组的名称;

    //不推荐
    元素的数据类型  一维数组名[];

举例:

    int[] arr;
    int arr1[];
    double[] arr2;
    String[] arr3;  //引用类型变量数组

Java语言中声明数组时不能指定其长度(数组中元素的个数)。 例如: int a[5]; //非法

2.2 一维数组的初始化

2.2.1 静态初始化

  • 如果数组变量的初始化和数组元素的赋值操作同时进行,那就称为静态初始化

  • 静态初始化,本质是用静态数据(编译时已知)为数组初始化。此时数组的长度由静态数据的个数决定。

静态初始化格式1:

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

静态初始化格式2:

    数据类型[] 数组名 = {元素1,元素2,元素3...};//必须在一个语句中完成,不能分成两个语句写

举例:

    int[] arr = new int[]{1,2,3,4,5};//正确

    int[] arr1;
    arr1 = new int[]{1,2,3,4,5};//正确

    int[] arr2 = {1,2,3,4,5};//正确

    int[] arr3;
    arr3 = {1,2,3,4,5};//错误

2.2.2 动态初始化

数组变量的初始化和数组元素的赋值操作分开进行,即为动态初始化

动态初始化中,只确定了元素的个数(即数组的长度),而元素值此时只是默认值,还并未真正赋自己期望的值。真正期望的数据需要后续单独一个一个赋值。

动态初始化格式:

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

或

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

注意:数组长度一旦指定,不可更改

举例:

    // 正确写法
    int[] arr = new int[5];

    int[] arr;
    arr = new int[5];

    // 错误写法
    // int[] arr = new int[5]{1,2,3,4,5}
    // 后面有{}指定元素列表,就不需要在[]中指定元素个数了。

2.3 一维数组的应用

(1)获取数组长度

    数组名.length

(2)访问数组元素

    数组名[索引/下标]

举例

public class ArrayTest {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};

        System.out.println("arr数组的长度:" + arr.length);
        System.out.println("arr数组的第1个元素:" + arr[0]);
        System.out.println("arr数组的第2个元素:" + arr[1]);
        System.out.println("arr数组的第3个元素:" + arr[2]);
        System.out.println("arr数组的第4个元素:" + arr[3]);
        System.out.println("arr数组的第5个元素:" + arr[4]);

        //修改第1个元素的值
        arr[0] = 100;
        System.out.println("arr数组的第1个元素:" + arr[0]);
    }
}

(3)一维数组的遍历

使用for循环可以遍历数组元素

public class ArrayTest2 {
    public static void main(String[] args) {
        int[] arr = new int[5];

        System.out.println("arr数组的长度:" + arr.length);
        System.out.print("存储数据到arr数组之前:[");
        for (int i = 0; i < arr.length; i++) {
            if(i==0){
                System.out.print(arr[i]);
            }else{
                System.out.print("," + arr[i]);
            }
        }
        System.out.println("]");

        for (int i = 0; i < arr.length; i++) {
            arr[i] = (i+1) * 2;
        }

        System.out.print("存储数据到arr数组之后:[");
        for (int i = 0; i < arr.length; i++) {
            if(i==0){
                System.out.print(arr[i]);
            }else{
                System.out.print("," + arr[i]);
            }
        }
        System.out.println("]");
    }
}

// 运行结果
// arr数组的长度:5
// 存储数据到arr数组之前:[0,0,0,0,0]
// 存储数据到arr数组之后:[2,4,6,8,10]

2.4 数组元素的默认值

数组是引用类型,当使用动态初始化方式创建数组时,元素值只是默认值。对于基本数据类型而言,默认初始化值各有不同。对于引用数据类型而言,默认初始化值为null

数组元素默认值
public class ArrayTest3 {
    public static void main(String argv[]){
        int a[]= new int[5]; 
        System.out.println(a[3]); //a[3]的默认值为0
    }
} 

2.5 数组的内存分析

2.5.1 Java虚拟机的内存划分

我们先初步领略下JVM的架构图(尚硅谷宋红康)

JVM架构-简图
区域名称 作用
虚拟机栈 用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度的各种基本数据类型、对象引用,方法执行完,自动释放。
堆内存 存储对象(包括数组对象),new创建的,都存储在堆内存。
方法区 存储已被虚拟机加载的类信息、常量、(静态变量)、即时编译器编译后的代码等数据。
本地方法栈 当程序中调用了native的本地方法时,本地方法执行期间的内存区域
程序计数器 程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址

2.5.2 一维数组在内存中的存储

public static void main(String[] args) {
    int[] arr = new int[4];
    System.out.println(arr);//[I@4eec7777
}
一维数组内存结构

具体细节:

  1. main()方法进入方法栈
  2. 创建数组,JVM会在堆内存中开辟连续空间,用以存储数组
  3. 数组在内存中会有自己的内存地址,以十六进制数表示
  4. 数组中有4个元素,int默认值为0
  5. JVM将数组的内存首地址赋值给引用类型变量arr(即数组名)

变量arr保存的是数组内存中的地址,而不是一个具体的数值,因此称为引用数据类型
数组下标从0开始,是因为第一个元素距离数组首地址间隔0个单元格。

当把一个数组名赋给另一个数组名时,此时两个数组变量本质上代表同一个数组,数组名都指向同一个内存地址

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

    System.out.println(arr1);//[I@4eec7777
    System.out.println(arr2);//[I@4eec7777
}

[I@4eec7777 中 [表示一维数组,I表示int类型,@后的值为内存的hash值

2.5.3 常见异常

(1)数组下标越界异常

当访问数组元素时,下标指定超出[0, 数组名.length-1]的范围时,就会出现数组下标越界异常:ArrayIndexOutOfBoundsException。在实际开发中,我们一定要避免出现这种情况。

public class ArrayTest4 {
    public static void main(String[] args) {
        int[] arr = {1,2,3};
       // System.out.println("最后一个元素:" + arr[3]);//错误,下标越界
      //  System.out.println("最后一个元素:" + arr[arr.length]);//错误,下标越界
        System.out.println("最后一个元素:" + arr[arr.length-1]);//正确
    }
}

(2)空指针异常

public class ArrayTest5 {
    public static void main(String[] args) {
        //定义数组
        int[][] arr = new int[3][];

        System.out.println(arr[0][0]);//NullPointerException
    }
}

因为此时数组的每一行还未分配具体存储元素的空间,arr[0]是null,此时访问arr[0][0]会抛出NullPointerException 空指针异常。

3. 二维数组

二维数组可以想象成一个棋盘,由行和列组成,每行就是一个一维数组。

3.1 二维数组声明

二维数组声明的语法格式:

    //推荐
    元素的数据类型[][] 二维数组的名称;

    //不推荐
    元素的数据类型  二维数组名[][];
    //不推荐
    元素的数据类型[]  二维数组名[];

3.2 二维数组初始化

3.2.1 静态初始化

    // 推荐写法
    int[][] arr = {{1,2,3},{4,5,6},{7,8,9,10}};//声明与初始化必须在一句完成

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

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

    arr = new int[3][3]{{1,2,3},{4,5,6},{7,8,9,10}};//错误,静态初始化右边new 数据类型[][]中不能写数字

注意:一种特殊写法情况:int[] x,y[]; x是一维数组,y是二维数组。

3.2.2 动态初始化

如果二维数组的每一个数据,甚至是每一行的列数,需要后期单独确定,那么就只能使用动态初始化方式了。动态初始化方式分为两种格式:

格式1:规则二维表:每一行的列数是相同的

    //(1)确定行数和列数
    数据类型[][] 二维数组名 = new 数据类型[m][n];

    //(2)再为元素赋新值
    二维数组名[行下标][列下标] = 值;

其中,m表示这个二维数组有多少个一维数组(行数),n表示每一个一维数组的元素有多少个(列数)

格式2:不规则:每一行的列数不一样

    //(1)先确定总行数
    数据类型[][] 二维数组名 = new 数据类型[总行数][];

    //此时只是确定了总行数,每一行里面现在是null

    //(2)再确定每一行的列数,创建每一行的一维数组
    二维数组名[行下标] = new 数据类型[该行的总列数];

    //此时已经new完的行的元素就有默认值了,没有new的行还是null

    //(3)再为元素赋值
    二维数组名[行下标][列下标] = 值;

举例:

/*
 1
 2 2
 3 3 3
 4 4 4 4
 5 5 5 5 5
 */
public class TwoDimensionaArrayTest1 {
    public static void main(String[] args){
        //1、声明一个二维数组,并且确定行数
        //因为每一行的列数不同,这里无法直接确定列数
        int[][]  arr = new int[5][];

        //2、确定每一行的列数
        for(int i=0; i<arr.length; i++){
        /*
        arr[0] 的列数是1
        arr[1] 的列数是2
        arr[2] 的列数是3
        arr[3] 的列数是4
        arr[4] 的列数是5
        */
            arr[i] = new int[i+1];
        }

        //3、确定元素的值
        for(int i=0; i<arr.length; i++){
            for(int j=0; j<arr[i].length; j++){
                arr[i][j] = i+1;
            }
        }

        //4、遍历显示
        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();
        }

    }
}

3.3 二维数组的应用

(1)获取数组长度

    二维数组名.length

(2)访问数组元素

    二维数组名[行下标][列下标]

(3)二维数组遍历

    for(int i=0; i<二维数组名.length; i++){ //二维数组对象.length
        for(int j=0; j<二维数组名[i].length; j++){//二维数组行对象.length
            System.out.print(二维数组名[i][j]);
        }
        System.out.println();
    }

例题: 使用二维数组打印一个 10 行杨辉三角。

提示:

  1. 第一行有 1 个元素, 第 n 行有 n 个元素

  2. 每一行的第一个元素和最后一个元素都是 1

  3. 从第三行开始, 对于非第一个元素和最后一个元素的元素。即:

   yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];

image-20220317005549522

public class YangHuiTest {
    public static void main(String[] args) {

        //1. 动态初始化的方式创建二维数组
        int[][] yangHui = new int[10][];

        for (int i = 0; i < yangHui.length; i++) {
            yangHui[i] = new int[i + 1];

            //2. 给数组元素赋值
            // 2.1 给外层数组元素中的首元素和末元素赋值
            yangHui[i][0] = yangHui[i][i] = 1;

            //2.2 给外层数组元素中的非首元素和非末元素赋值(难)
            //if(i > 1){ //从 i == 2 开始执行
                for(int j = 1;j < yangHui[i].length - 1;j++){ //非首元素和非末元素的角标范围
                    yangHui[i][j] = yangHui[i-1][j-1] + yangHui[i-1][j];

                }
            //}
        }


        //3. 遍历二维数组
        for (int i = 0; i < yangHui.length; i++) {
            for (int j = 0; j < yangHui[i].length; j++) {
                System.out.print(yangHui[i][j] + "\t");
            }

            System.out.println();
        }

    }
}

3.4 二维数组内存解析

二维数组本质上是元素类型是一维数组的一维数组。

    int[][] arr = {
        {1},
        {2,2},
        {3,3,3},
        {4,4,4,4},
        {5,5,5,5,5}
    };
二维数组内存结构

4. Arrays工具类的使用

java.util.Arrays类即为操作数组的工具类,包含了用来操作数组的各种方法。

(1)打印数组元素:static String toString(int[] a)
返回元素列表,括在方括号("[]")中,相邻元素用字符 ", "(逗号加空格)分隔。形式为:[元素1,元素2,元素3···]

import java.util.Arrays;
public class ArrayToStringTest {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5,6};
        System.out.println("arr = " + Arrays.toString(arr));
    // arr = [1, 2, 3, 4, 5, 6]
    }
} 

注意:多维数组打印使用Arrays.deepToString()

(2)数组排序

  • static void sort(int[] a) :将a数组按照从小到大进行排序
  • static void sort(int[] a, int fromIndex, int toIndex) :将a数组的[fromIndex, toIndex)部分按照升序排列
  • static void sort(Object[] a) :根据元素的自然顺序对指定对象数组按升序进行排序。
  • static <T> void sort(T[] a, Comparator<? super T> c) :根据指定比较器产生的顺序对指定对象数组进行排序。
import java.util.Arrays;
public class ArraySortTest {
    public static void main(String[] args) {
        int[] arr = {3, 2, 5, 1, 6};
        System.out.println("排序前" + Arrays.toString(arr));// 排序前[3, 2, 5, 1, 6]
        Arrays.sort(arr);
        System.out.println("排序后" + Arrays.toString(arr));//排序后[1, 2, 3, 5, 6]
    }
} 

(3)二分查找

  • static int binarySearch(int[] a, int key)
  • static int binarySearch(Object[] a, Object key)
    要求数组有序,在数组中查找key是否存在,如果存在返回第一次找到的下标,不存在返回负数。
import java.util.Arrays;
public class ArrayBinarySearchTest {
    public static void main(String[] args) {
        int[] arr = {1,3,5,7,9,20,108};
        System.out.println(Arrays.binarySearch(arr, 9));// 4
    }
} 

(4)比较两个数组是否相等

  • static boolean equals(int[] a, int[] a2) :比较两个数组的长度、元素是否完全相同
  • static boolean equals(Object[] a,Object[] a2):比较两个数组的长度、元素是否完全相同
import java.util.Arrays;
public class ArrayEqualsTest {
    public static void main(String[] args) {
        int[] arr1 = {1,3,5,7,9,20,108};
        int[] arr2 = {1,2,3,4,5,6,7};
        int[] arr3 = {1,2,3,4,5,6,7};
        System.out.println(Arrays.equals(arr1,arr2));// false
        System.out.println(Arrays.equals(arr2,arr3));// true
    }
} 

(5)数组填充

  • static void fill(int[] a, int val) :用val值填充整个a数组
  • static void fill(Object[] a,Object val):用val对象填充整个a数组
  • static void fill(int[] a, int fromIndex, int toIndex, int val):将a数组[fromIndex,toIndex)部分填充为val值
  • static void fill(Object[] a, int fromIndex, int toIndex, Object val) :将a数组[fromIndex,toIndex)部分填充为val对象
import java.util.Arrays;
public class ArrayFillTest {
    public static void main(String[] args) {
        int[] arr = new int[5];
        Arrays.fill(arr,2);
        System.out.println(Arrays.toString(arr));// [2, 2, 2, 2, 2]
        
        int[] arr1 = new int[10];
        Arrays.fill(arr1,2,8,10);
        System.out.println(Arrays.toString(arr1));// [0, 0, 10, 10, 10, 10, 10, 10, 0, 0]
    }
} 

本文作者:-Miao酱-

本文链接:https://www.cnblogs.com/miaotechisland/p/18634941

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   -Miao酱-  阅读(2)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起