【JavaSE】数组

目录

1. 何为数组

2. 数组的创建及初始化

    2.1 数组的创建

    2.2 数组的初始化

3. 数组的使用

    3.1 数组的访问

    3.2 遍历数组

4. JVM的内存分布

5. 引用类型变量——数组详解

    5.1 基本类型变量与引用类型变量的区别

    5.2 Java中的null

6. 一维数组的例题

7. 二维数组

    7.1 二维数组的语法

    7.2 不规则的二维数组


1. 何为数组

数组,可以看成是相同类型元素的一个集合。在内存中是一段连续的空间。每个空间有自己的编号,起始位置的编号为 0 ,即数组的下标。

2. 数组的创建及初始化

2.1 数组的创建

在数组中存放元素的类型[] 数组名 = new 类型[数组长度]

   

具体例子:

int[] array = new int[10];  //创建一个可以容纳10个int类型的数组

double[] array1 = new double[5];  //创建一个可以存放5个double类型的数组

String[] array2 = new String[2];  //创建一个可以存放2个字符串的数组

2.2 数组的初始化

数组的初始化主要分为动态初始化以及静态初始化。

动态初始化:在创建数组时,直接指定数组中元素的个数

short[] array = new short[5]; 

静态初始化:在创建数组时不直接指定数据元素个数,而直接将具体的数据内容进行指定

在数组中存放元素的类型[] 数组名 = {data1, data2, data3, ......datan};

 

具体例子:

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

double[] arr2 = {1.2, 2.3, 2.5, 5.6, 4.9};

String[] arr3 = new String[]{"What's up!", "Brother!", "hey hey hey"}; 

String[] arr3 = {"What's up!", "Brother!", "hey hey hey"}; 

注意:

1. 静态初始化虽然没有指定数组的长度,编译器在编译时会根据 { } 中元素个数来确定数组的长度。

2. 静态初始化时, { } 中数据类型必须与[]前数据类型一致。

3. 静态初始化可以简写,省去后面的 new T[] 。

两种初始化方式都可以分两步写,但这样的话,new 类型名[] 不能省略

int[] arr1;

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

  

int[] arr2;

arr2 = new int[10];

如果没有对数组进行初始化,数组中元素有其默认值

如果是基本数据类型:

类型默认值
byte0
short0
int0
long0
float0.0f
double0.0
char/u0000
booleanfalse

如果数组中存储元素类型为引用类型,默认值为 null。

3. 数组的使用

3.1 数组的访问

数组在内存中是一段连续的空间,空间的编号都是从0开始的,依次递增,该编号称为数组的下标,数组可以通过下标访问其任意位置的元素。在数组中可以通过数组对象.length 来获取数组的长度

    public static void main(String[] args) {
        int[] array = {12, 5, 34, 57, 23, 21, 90, 12};
        System.out.println(array[2]);
        array[3]=666;
        System.out.println(array[3]);
        System.out.println(array.length);
    }

 输出:34  666 8

注意:

1. 数组是一段连续的内存空间,因此支持随机访问,即通过下标访问快速访问数组中任意位置的元素

2. 下标从 0 开始,介于[0, N)之间不包含 N,N 为元素个数,不能越界,否则会报出下标越界异常。

3.2 遍历数组

    public static void main(String[] args) {
        double[] array = {12.3 , 55.7 , 33.6 , 66.6 };
        int i = 0;
        for (i = 0; i < array.length; i++) {
            System.out.print(array[i]+" ");
        }
        System.out.println();
        for(double x : array){
            System.out.print(x+" ");
        }
    }

for-each 是 for 循环的另外一种使用方式。能够更方便的完成对数组的遍历。可以避免循环条件和更新语句写错。

4. JVM 的内存分布

内存是一段连续的存储空间,主要用来存储程序运行时数据的。

1. 程序运行时代码需要加载到内存

2. 程序运行产生的中间数据要存放在内存

3. 程序中的常量也要保存

4. 有些数据可能需要长时间存储,而有些数据当方法运行结束后就要被销毁

如果对内存中存储的数据不加区分的随意存储,内存管理起来将会非常麻烦。因此 JVM 也对所使用的内存按照功能的不同进行了划分:

程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址

虚拟机栈(JVM Stack): 与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一 些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。

本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的

堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2, 3} ),堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。

方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域 

现在我们只简单关心 虚拟机栈 这两块空间。

5. 引用类型变量——数组详解

5.1 基本类型变量与引用类型变量的区别

基本数据类型创建的变量,称为基本变量,直接存放的是数值;而引用类型变量创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址

    public static void fun1(){
        int a = 1;
        int b = 2;
        int[] array = new int[3];
    } 

a,b,array 都是函数内部的变量,因此其空间都在 main 方法对应的栈帧中分配。不同的是,a 和 b 是基本类型,存放的是数值,而 array 是引用类型,存放的可以简单理解成是数组在堆空间中的首地址。

从上图可以看到,引用变量并不直接存储对象本身,可以简单理解成存储的是对象在堆中空间的起始地址。通过该地址,引用变量便可以去操作对象。有点类似 C 语言中的指针,但是 Java 中引用要比指针的操作更简单。

引用变量的这种性质,可以有以下的情况:

1. 引用变量 arr1,arr2 共同指向同一块空间,两者都访问这个数组。

    public static void main(String[] args) {
        int[] arr1 = {1,2,3,4,5};
        int[] arr2 = arr1;
        arr2[0] = 6666;
        System.out.println(Arrays.toString(arr1));
        System.out.println(Arrays.toString(arr2));
     }

 输出 

         [6666, 2, 3, 4, 5]
         [6666, 2, 3, 4, 5]

2. 两个引用变量 arr1 和 arr2 各自存放不同的地址,分别访问两个不同的数组;可当 arr1 = arr2 时,arr1 存放了 arr2 存放的地址。arr1 与 arr2 指向同一块空间,而之前 arr1 的空间被销毁了。

    public static void main(String[] args) {
        int[] arr1 = {1,2,3,4,5};
        int[] arr2 = {4,6,8,9,11};
        arr1 = arr2;
        System.out.println(Arrays.toString(arr1));
        System.out.println(Arrays.toString(arr2));
    }

 输出 

         [4, 6, 8, 9, 11]
         [4, 6, 8, 9, 11]

3. 数组传参,传的是地址,可以通过形参修改指定对象的值,从而改变实参。但如果后续方法体改变了形参的指向,那么形参的修改影响不到实参:

    public static void fun2(int[] array){
        array[0] = 9999;
    }

    public static void main(String[] args) {
        int[] array1 = {2,4,6,8,2};
        fun2(array1);
        System.out.println(Arrays.toString(array1));
    }

 输出

        [9999, 4, 6, 8, 2]

    public static void fun1 (int[] array){
        array = new int[]{23,56,79};
    }
    public static void main(String[] args) {
        int[] array1 = {2,4,6};
        fun1(array1);
        System.out.println(Arrays.toString(array1));
    }

输出:

         [2, 4, 6]

解析:

在还未执行 fun1 函数体之前,array 存的是 array1 存的地址:

 而 fun1 方法体中,令 array 重新 new了一个数组,令 array 改变了指向:

 这时不管 array 如何修改,都不会影响到 array1 。

示例:

1. 将数组 array 里的值,都 2 倍输出。基于上面讲述的内容,我们可以有两种写法:

    public static void doubleOfArray(int[] arr){
        for (int i = 0; i < arr.length; i++) {
            arr[i] = 2 * arr[i];
        }
    }
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6,7};
        doubleOfArray(array);
        System.out.println(Arrays.toString(array));
    }

 输出

         [2, 4, 6, 8, 10, 12, 14]

    public static int[] doubleOfArray1(int[] arr){
        int[] tempArray = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            tempArray[i] = 2 * arr[i];
        }
        return tempArray;
    }

    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6,7};
        int[] ret = doubleOfArray1(array);
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(ret));
    }

 输出 

         [1, 2, 3, 4, 5, 6, 7]
         [2, 4, 6, 8, 10, 12, 14]

5.2 Java 中的 null

null 在 Java 中表示 "空引用" , 也就是一个不指向对象的引用。null 的作用类似于 C 语言中的 NULL (空指针), 都是表示一个无效的内存位置. 因此不能对这个内存进行任何读写操作。 一旦尝试读写, 就会抛出 NullPointerException,即空指针异常

    public static void main(String[] args) {
        int[] array = null;
        System.out.println(array); 
        System.out.println(array.length);
    }

6. 一维数组的例题

1. 自己写一个 Arrays.toString 方法

    public static String myToString(int[] arr){
        String ret = "[";
        for (int i = 0; i < arr.length; i++) {
            ret += arr[i];
            if(i != arr.length-1){
                ret += ", ";
            }
        }
        ret += "]";
        return ret;
    }
    public static void main(String[] args) {
        int[] array = {1,7,0,36,9};
        String ret = myToString(array);
        System.out.println(ret);
    }

2. 数组的拷贝

Arrays.copyOf() 的使用

    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        int[] arr1 = Arrays.copyOf(arr,arr.length);
        System.out.println(Arrays.toString(arr));
        System.out.println(Arrays.toString(arr1));
        arr1[0] = 999;
        System.out.println(Arrays.toString(arr));
        System.out.println(Arrays.toString(arr1));
    }

Arrays.copyOf() 还可以扩容:

    public static void main(String[] args) {
        int[] arr = {2,8,1,4};
        int[] ret = Arrays.copyOf(arr,2*arr.length);
        System.out.println(Arrays.toString(arr));
        System.out.println(Arrays.toString(ret));
    }

输出

        [2, 8, 1, 4]
        [2, 8, 1, 4, 0, 0, 0, 0]

Arrays.copyOfRange() 还可以指定拷贝的范围,通常是个左闭右开的区间

    public static void main(String[] args) {
        int[] arr = {1,2,3,4};
        int[] ret = Arrays.copyOfRange(arr,2,4);
        System.out.println(Arrays.toString(arr));
        System.out.println(Arrays.toString(ret));
    }

输出 

        [1, 2, 3, 4]
        [3, 4]

System.arraycopy() 可以规定拷贝的位置:

    public static void main(String[] args) {
        int[] arr = {66,77,88,99};
        int[] ret = new int[arr.length];
        System.arraycopy(arr,1,ret,1,arr.length-1);
        System.out.println(Arrays.toString(arr));
        System.out.println(Arrays.toString(ret));
    }

输出:

         [66, 77, 88, 99]
         [0, 77, 88, 99]

3. 两个数组是否相等

    public static void main(String[] args) {
        int[] arr1 = {11,11,22,33,44};
        int[] arr2 = {11,11,22,33,45};
        System.out.println(Arrays.equals(arr1,arr2));
    }

输出

       false

4. 填充某个数到一个数组

        int[] arr3 = new int[10];
        Arrays.fill(arr3,2,7,7);
        System.out.println(Arrays.toString(arr3));

输出

        [0, 0, 7, 7, 7, 7, 7, 0, 0, 0]

7. 二维数组

7.1 二维数组的语法

数据类型[][] 数组名 = new 数据类型[行数][列数] {初始化数据};

数据类型[][] 数组名 = {初始化数据};

 遍历二维数组的方式

    //二维数组
    public static void main(String[] args) {
        int[][] arr = new int[][]{{1,2,3},{4,5,6}};
        //或者
        int[][] arr1 = {{1,2,3},{2,3,4}};
        int i = 0;
        int j = 0;
        //遍历二维数组
        for (i = 0; i < 2; i++) {
            for (j = 0; j < 3; j++) {
                System.out.print(arr[i][j]+" ");
            }
            System.out.println();
        }
        System.out.println("#####################");
        for ( i = 0; i < arr.length; i++) {
            for (j = 0; j < arr[0].length; j++) {
                System.out.print(arr[i][j]+" ");
            }
            System.out.println();
        }
        System.out.println("########################");
        for(int[] ret : arr){
            for(int x : ret){
                System.out.print(x+" ");
            }
            System.out.println();
        }
        System.out.println("###########################");
        System.out.println(Arrays.deepToString(arr));
    }

二维数组更像是一个特殊的一维数组

arr[0] , arr[1] 分别存放了 0x11 与 0x12 这两个地址,而这两个地址其实是两个一维数组首元素的地址。所以 arr.length 得到的是行数 2 ,而 arr[0].length 得到的是列数3 。

7.2 不规则的二维数组

c 语言中的二维数组,行数能省,列不能省。但在 Java 中,列能省,但行不能省,更有甚者,每行的列数可以不同。

    //不规则二维数组
    public static void main(String[] args) {
        int[][] arr = new int[3][];
        arr[0] = new int[]{1,2,3,4,5};
        arr[1] = new int[]{2,3,4};
        arr[2] = new int[]{7,3,2,1,3,5,6};
        System.out.println(Arrays.deepToString(arr));
    }

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值