你真的学会了JavaSE中数组的定义与使用吗?

🎐 前情提要

本节目标:

  1. 理解数组基本概念
  2. 掌握数组的基本用法
  3. 数组与方法互操作
  4. 熟练掌握数组相关的常见问题和代码

接下来让我们开始学习Java中数组的定义与使用吧!


👩🏻‍🏫作者: 初入编程的菜鸟哒哒
📚系列文章目录:

一、TvT——JavaSE(1)–Java的数据类型与变量
二、TvT——JavaSE(2)–Java的运算符
三、TvT——JavaSE(3)–Java的逻辑控制
四、TvT——JavaSE(4)–Java方法的使用


📜刷题笔记:
1️⃣TvT——C语言进阶の刷题【1】:指针进阶



1. 数组的基本概念

1.1 为什么要使用数组

如果我们想定义3个整型数据,你可能会这样定义:

public static void main(String[] args) {
        int a = 8;
        int b = 6;
        int c = 9;
}

当然,这样定义是没有问题的。
但如果我们想定义10个整型数据呢?
很显然一个一个定义就显得有些麻烦…
在java当中有一个构造数据类型:数组
当你想创建10个变量并且他们是相同类型的时候,我们就可以通过数组来进行定义。

1.2 什么是数组

数组:可以看成是相同类型元素的一个集合。在内存中是一段连续的空间。

数组的特点:

  1. 数组中存放的元素其类型相同
  2. 数组的空间是连在一起的
  3. 每个空间有自己的编号,其实位置的编号为0,即数组的下标。

那在程序中如何创建数组呢?

1.3 数组的创建及初始化

第一种定义方式:

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

这样就定义好了一个数组。 int[]代表一个整型的数组类型,arr是数组名,虽然我们没有指定数组的长度,但编译器在编译时会根据{}中元素个数来确定数组的长度。

第二种定义方式:

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

第三种定义方式:

public static void main(String[] args) {
        int[] arr = new int[5];//这里面的5是数组的长度
}

这三种定义方式在内存中的存储方式都是一样的,第一种只不过是第二种的一种简写,第三种里面没有存储数据,默认为里面全都是0(boolean默认为false)。
在这里插入图片描述
如果不确定数组当中内容时,应该使用动态初始化。
为什么呢?
因为数组是没办法直接更改里面的所有元素的:
比如:
在这里插入图片描述
这样写是不被允许的。

以定义方法一为例,我们直接打印数组名System.out.println(arr); 可以打印出整个数组吗?
可以看见结果不是整个数组:
在这里插入图片描述
在这里插入图片描述
这个时候我们可以发现arr虽然是变量,但里面存了一个地址,这个时候就把这个变量叫做引用变量。
所以我们把里面存地址的变量都叫做引用。

关键字new

我们通过new关键字来创建一个对象。
引用里面存储了对象的地址,我们通常说: 引用指向了/引用了一个对象

1.4 如何访问数组当中的元素

数组名[合法的下标]

举个栗子:

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

在这里插入图片描述
也可以修改:

 public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        System.out.println(arr[0]);
        arr[0] = 9;
        System.out.println(arr[0]);
}

在这里插入图片描述
那什么叫合法的下标呢?

比如如果我把arr[0]改成arr[5](不合法的下标):
这个时候就会报错:
在这里插入图片描述
如果再初始化只能改成动态的:
在这里插入图片描述

1.5 如何求数组长度:

只需要调用length函数:数组名.length

public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        int len = arr.length;//这个就是数组长度
        System.out.println(len);
}

执行结果:
在这里插入图片描述

1.5 如何遍历数组:

所谓 “遍历” 是指将数组中的所有元素都访问一遍, 访问是指对数组中的元素进行某种操作。
比如:打印。

第一种打印方式:
使用for循环
在这里插入图片描述
第二种打印方式:
使用for each
在这里插入图片描述

for(int x : arr) {
            System.out.println(x);
}

上面代码的意思是把arr的每项都存在x中,存一个打印一个,存一个打印一个。

第三种打印方式:
调用Arrays中的toString函数,将传进去的数组转化为字符串。
在这里插入图片描述
在这里插入图片描述

2. 数组是引用类型

2.1 初始JVM的内存分布

JVM一共有五块内存:
我们平时嘴边上说的(接触到的)栈都是Java虚拟机栈,堆也是我们现阶段经常用到的。

在这里插入图片描述
那什么叫线程隔离的数据区呢?
在多线程中,每一个线程都有方法区,程序方法区、程序计算器(单独的线程隔离的数据区),而Java数据栈、堆(所有线程共享的数据区)是各个线程共用的。

  • 程序计数器:只是一个很小的空间,保存下一条执行指令的地址。
  • 虚拟机栈:我们平时说的局部变量在栈上,方法在栈上开辟内存,所指的就是虚拟机栈。与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。
  • 本地方法栈:执行的是Native方法,是C/C++实现的,在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的。
  • 堆:比如我们说的malloc在堆上分配内存,是JVM所管理的最大的内存区域,使用new创建的对象都是堆上保存。堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
  • 方法区:存储当前类的一些信息,包含已被虚拟机加载的类信息(主要存储)、常量、静态变量、即时编译器编译后的代码等数据。方法编译出的的字节码就是保存在这个区域。

举例讲解一下:
在这里插入图片描述
这块代码在内存中的分配情况是怎么样的呢?
在这里插入图片描述
在执行main函数的时候我们会在栈中给main函数开辟一片栈帧。
在这里插入图片描述
这个时候我们就说arr这个引用指向那个堆上的对象。
到这里就能看出来这个通过new(省略了)创建的对象其实存储在堆上。

2.2 再谈引用变量

看这个代码:

public static void func() {
        int[] array1 = new int[3];
        array1[0] = 10;
        array1[1] = 20;
        array1[2] = 30;
        int[] array2 = new int[]{1,2,3,4,5};
        array2[0] = 100;
        array2[1] = 200;
        array1 = array2;
        array1[2] = 300;
        array1[3] = 400;
        array2[4] = 500;
        for (int i = 0; i < array2.length; i++) {
            System.out.println(array2[i]);
        }
    }
    public static void main(String[] args) {
        func();
    }

在执行arrar1 = array2 语句之前,java虚拟机的数据区是这样的。

在这里插入图片描述
执行了之后,array1转为指向array2

在这里插入图片描述
所以下面的array1[2]array1[3]指向的其实就是array2[2]array2[3],array2的指向没有发生改变,所以array2[4]还是指向array2[4]
赋值之后堆上数据变成这样:
在这里插入图片描述
array2打印出来:
在这里插入图片描述

2.3 认识 null

java中的局部变量我们在使用的时候一定要初始化。
在这里插入图片描述

那如果这个变量是一个引用变量我们怎样给它初始化呢?

int[] arr3 = null;

意思是:array3这个引用不指向任何对象。

那这样写有问题吗?

int[] arr3 = null;
System.out.println(arr3.length);

答案是不可以的:
在这里插入图片描述
编译器给我们报了一个空指针异常。

有一个很棒的方法排查空指针异常:
就是在编译器指向的那一行找.号,一般空指针异常都是用空指针.了一个东西。

或者:
在这里插入图片描述
这样也会报空指针异常。
因为arr3这个引用是没有指向对象的

接下来大家做一道题:
下面这个代码的执行结果是什么呢?

public static void func1(int[] array) {
        array = new int[]{1,2,3};
    }
    public static void func2(int[] array) {
        array[0] = 99;
    }

    public static void main(String[] args) {
        int[] array = {9,8,7};
        func1(array);
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i]+ " ");
        }
        System.out.println();
        System.out.println("===========");
        func2(array);
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i]+ " ");
        }
        System.out.println();

    }

答案是:
在这里插入图片描述
为什么func1()没有改变array引用所指向对象里面的值,而func2()就改变了呢?
因为

public static void func1(int[] array) {
        array = new int[]{1,2,3};
    }

这个fun1()函数形参array一开始确实存的是实参array的值,但是array = new int[]{1,2,3};这段代码改变了形参array的指向,是在堆中又建立了一个{1,2,3}这个数组,形参array指向了这个新数组,并且出函数就销毁了。
fun2()中的形参array没有改变,所以就直接改变实参array指向的数组中的数据了。

3. 数组拷贝

拷贝:

  1. 要有原来的
  2. 拷贝出来一个新的

非常简单:一个一个放进去就可以了
在这里插入图片描述
第一种方法:

public class TestDemo {
    public static int binarySearch(int[] arr,int num) {
        int left = 0;
        int right = arr.length;
        while(left<=right) {
            int mid = (left+right)/2;
            if(arr[mid]<num) {
                left = mid+1;
            }else if(arr[mid]>num) {
                right = mid - 1;
            }else {
                return mid;
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5,6,7};
        int ret = binarySearch(arr,6);
        System.out.println(ret);
    }
}

难道以后拷贝都用这个方法吗?
不是的。

第二种方法:
可以使用java当中提供的工具类:
copyOf方法在进行数组拷贝时,创建了一个新的数组。
在这里插入图片描述

全部拷贝:

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

拷贝一部分:

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

在这里插入图片描述
扩容:
在这里插入图片描述

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

在这里插入图片描述
第三种方法:
也可以使用Arrays.copyOfRange()
可以拷贝一部分:
在Java中一般使用from都是左闭右开的。在这里插入图片描述
第四种方法:

System.arraycopy();

在这里插入图片描述

System.arraycopy();是native方法。

native : C/C++实现的方法。
优点: 快!

System.arraycopy()参数:

  1. 你要拷贝的数组
  2. 你要从这个数组的哪个下标开始访问
  3. 你要拷贝到哪个数组
  4. 你要拷贝到这个数组的哪个位置开始
  5. 你要拷贝多大

可以接受任意类型,因为他是祖先类。

下面这个代码叫拷贝吗?

int[] arr = {1,2,3,4,5,6}; 
int[] copy = arr; 
System.out.println(Arrays.toString(copy));

在这里插入图片描述
根本没有发生拷贝,
只是copy这个引用指向了arr这个引用指向的对象。

简单介绍一下浅拷贝深拷贝:

如果能够做到修改拷贝之后的数组不影响原来的数组,就说这个拷贝是深拷贝

如果arr指向的对象里面的每一个元素都是一个引用,那copy所指向的对象里面的每一个元素也都是一个引用。这时我们写这个代码:copy[0].a = 9;就影响了原来的数组,这就是浅拷贝

深拷贝浅拷贝不是说拷贝基本数据类型还是引用类型,具体需要看代码的实现。

拿上面的例子来说如果拷贝arr的时候把arr所指对象 指向的对象也全部拷贝下来了的话,改变copy还是不会改变arr,这就变成了深拷贝。

4.数组练习

4.1求数组中元素的平均值

public static double avg(int[] array) {
        int sum = 0;
        for(int x:array) {
            sum+=x;
        }
        return sum*1.0/array.length;
    }
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,7};
        System.out.println(avg(arr));
    }

在这里插入图片描述

4.2 查找数组中指定元素(顺序查找)

给定一个数组, 再给定一个元素, 找出该元素在数组中的位置.
查找数组中的某个元素:

最简单的顺序查找:

 public static int findKey(int key,int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] == key) {
                return i;
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        int index = findKey(3,array);
        System.out.println(index);
    }

在这里插入图片描述

二分查找:

public class TestDemo {
    public static int binarySearch(int[] arr,int num) {
        int left = 0;
        int right = arr.length;
        while(left<=right) {
            int mid = (left+right)/2;
            if(arr[mid]<num) {
                left = mid+1;
            }else if(arr[mid]>num) {
                right = mid - 1;
            }else {
                return mid;
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5,6,7};
        int ret = binarySearch(arr,6);
        System.out.println(ret);
    }
}

在这里插入图片描述

4.3 数组排序(冒泡排序)

import java.util.Arrays;

public class TestDemo {
    public static void bubbleSort(int[] arr) {
        int len = arr.length;
        for (int i = 0; i < arr.length-1; i++) {
            int flag = 0;
            for (int j = 0; j < arr.length-1-i; j++) {
                if(arr[j]>arr[j+1]) {
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                    flag = 1;
                }
            }
            if(0 == flag) {
                break;
            }
        }
    }
    public static void main(String[] args) {
        int[] arr = {2,3,4,1,7,5};
        bubbleSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

在这里插入图片描述
冒泡排序性能较低。 Java 中内置了更高效的排序算法:

public static void main(String[] args) { 
    int[] arr = {9, 5, 2, 7}; 
    Arrays.sort(arr); 
    System.out.println(Arrays.toString(arr)); 
}

4.4 数组逆序

给定一个数组, 将里面的元素逆序排列

public static void main(String[] args) { 
    int[] arr = {1, 2, 3, 4}; 
    reverse(arr); 
    System.out.println(Arrays.toString(arr)); 
}
public static void reverse(int[] arr) { 
    int left = 0; 
    int right = arr.length - 1; 
    while (left < right) { 
        int tmp = arr[left]; 
        arr[left] = arr[right]; 
        arr[right] = tmp; 
        left++;
        right--; 
    }
}

5. 二维数组

二维数组本质上也就是一维数组, 只不过每个元素又是一个一维数组。
也可以理解为二维数组每行都存的是一个数组的引用。
在这里插入图片描述

定义二维数组的三种方法:

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

第一种遍历方式:

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

在这里插入图片描述

二维数组是特殊的一维数组。
第二种遍历方式:

for (int [] tmp:array) {
            for (int x:tmp) {
                System.out.print(x+" ");
            }
System.out.println();
}

在这里插入图片描述
第三种遍历方式:

System.out.println(Arrays.deepToString(array));

在这里插入图片描述
二维数组的用法和一维数组并没有明显差别, 因此我们不再赘述。

总结

在这里插入图片描述

本篇到此结束
“莫愁千里路,自有到来风。”
我们顶峰相见!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值