5-数组的定义与使用

本文介绍了Java中的数组,包括数组的基本概念,如何创建和初始化数组,以及动态和静态初始化的区别。此外,还讨论了数组的使用,如元素访问、遍历方法,以及数组作为引用类型在内存中的分布和管理。文章还提到了基本类型变量和引用类型变量的区别,并通过实例解释了引用变量的工作原理。最后,文章探讨了二维数组的创建和遍历,以及一些实用的数组工具方法,如Arrays类的使用。
摘要由CSDN通过智能技术生成

一.数组的基本概念

1.1 什么是数组

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

好比停车场的车位

从上图中可以看到

  • 数组中存放的元素其类型相同

  • 数组的空间是连在一起的

  • 每个空间有自己的编号,起始位置的编号为0,即数组的下标。

1.2 数组的创建及初始化

1.2.1 数组的创建

T[] 数组名 = new T[N];

T:表示数组中存放元素的类型

T[]:表示数组的类型

N:表示数组的长度,可以是变量

例如

int[] array=new int[5] ; //创建一个可以容纳5个int元素的数组
char[] array2=new char[10]; //创建一个可以容纳10个char元素的数组

1.2.2 数组的初始化

数组的初始化有两种

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

T[] array=new T[N]

2.静态初始化,在创建数组时不直接指定元素个数,而是根据给定的数据内容指定

T[] 数组名称={data1,data2,...}
或者
T[] 数组名称=new T[]{data1,data2,...}

例如

int[] array1 = new int[]{0,1,2,3,4,5,6,7,8,9};
double[] array2 = new double[]{1.0, 2.0, 3.0, 4.0, 5.0};
String[] array3 = new String[]{"hell", "Java", "!!!"};

注意

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

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

  • 静态初始化可以简写,省去后面的new T[](实际在编译过程中简写会被替换)。

除非是动态初始化,否则[]里不能出现数字

int[5] array={1,2,3,4,5}; //error
int[] array=new int[5]{1,2,3,4,5};//error

数组也可以按照C语言的方式这样创建

int array[5]={1,2,3,4,5};

但是并不推荐,就像定义一个整型变量 int a=10;我们很清晰地知道a的类型是int

Java中也可以很清楚地显示array的数据类型是int[](C语言的定义方式并不这么直观)

静态初始化和动态初始化都可以分为两步,但是简写的静态初始化不可以

int[] array1;
array1=new int[10];//动态初始化
int[] array2;
array2=new int[]{1,2,3,4,5};//静态初始化
int[] array3;
array3={1,2,3,4,5};//error, 当整体初始化时只能在定义时初始化,这样写并不能定义数组

如果没有对数组进行初始化,数组元素有自己的默认值

情况一.数组元素是基本数据类型

类型

默认值

byte

0

short

0

int

0

long

0

float

0.0f

double

0.0

char

/u0000(其实就是'0')

boolean

false

情况二.数据元素是引用类型,默认值为null

1.3 数组的使用

1.3.1 数组元素的访问

数组在内存中是一段连续的空间,空间的编号都是从0开始的,依次递增,该编号称为数组的下标,数组可以通过下标访问和修改其任意位置的元素
int[]array = new int[]{10, 20, 30, 40, 50};
System.out.println(array[0]);
System.out.println(array[1]);
System.out.println(array[2]);
System.out.println(array[3]);
System.out.println(array[4]);

// 也可以通过[]对数组中的元素进行修改
array[0] = 100;
System.out.println(array[0]);

注意,下标从0开始,到N-1结束,不能越界,否则会抛出异常

char[] array2=new char[10];
System.out.println(array2[15]);

红色箭头表示数组越界异常

1.3.2 数组的遍历

所谓 "遍历" 是指将数组中的所有元素都访问一遍, 访问是指对数组中的元素进行某种操作

方法一

通过数组名.length来获取数组长度,然后用for循环进行遍历

int[] array = new int[]{10, 20, 30, 40, 50};
for(int i = 0; i < 5; i++){
System.out.println(array[i]);
}

方法二

用for-each遍历数组

int[] array={1,2,3,4,5};
for(int e:array) {
System. out.println(e);
}

for-each又名“增强版for循环”,它表示将数组的每个元素取出来放到e里

方法三

import java.util.Arrays;
int[] array={1,2,3,4,5};
System. out.println(Arrays. toString(array));
//toString表示将array数组里的值按照字符串形式打印,目前只需要了解该方法即可

二. 数组是引用类型

2.1 JVM内存分布

过年的时候妈妈去走亲戚,你因为沉迷手机不想去,经过一番苦苦劝告,你还是没有跟妈妈同行,于是你在家里逍遥了几天---吃饭-打游戏-睡觉-吃饭...然后你家的厨房就变成了这样

妈妈终于回家了,看到这么脏的厨房,她也凌乱了,先是把你揍了一顿,然后勤劳地收拾了,于是厨房变成了这样

和你家的厨房一样,如果存储的数据不加区分地随意存储,那么管理起来会非常的麻烦

于是JVM也对所使用的的内存按照功能进行了划分:

如果想详细了解一下各个区的功能,可以仔细阅读下面的内容,如果不想,现在只需要记住我们今天的关键是虚拟机栈

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

2.2 基本类型变量和引用类型变量的区别

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

局部变量--定义在方法内部的变量,都存放在虚拟栈上,它们的生命周期是方法的创建->方法的结束,作用域在{}内

public static void func() {
int a=10;
int[] array={1,2,3,4,5};
}

上面的代码中,因为变量a和array是在方法内部创建的,因此被分配在虚拟机栈上

a是基本数据类型,它的内存空间保存的是自己的值10

array是引用数据类型,它的内存空间可以简单地理解为保存的所指对象的地址

因此,引用变量不直接存储对象本身,而是通过存储对象的地址操纵对象,有点类似于C语言中的指针,但引用要比指针的操作更简单

2.3 详解引用变量

来看下面一组代码,结合图解,帮助更好地理解引用变量

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]);
}
}

这组代码先是把array2的值赋给了array1,也就是说,array1也指向了array2指向的对象,即对象2有两个引用

然后对象2的两个引用变量分别修改了它的值,所以最终的打印结果应该是100,200,300,400,500

问:那么对象1呢?

答:

对象1没有引用变量指向它,因此系统会自动回收空间

2.4 认识空指针null

null在Java中表示空引用,也就是 一个不指向对象的引用
int[] array=null;
array[0]=1;
System.out.println(array.length);

上面的代码会出现空指针异常,因为array没有指向任何对象,所以不存在array[index],array也没有length这个属性

空指针异常

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

但是与C语言不同的是,Java并没有定义null与0号地址有任何关联

三.数组的应用场景

3.1 保存数据

当数据的类型相同的时候,用数组保存

public static void main(String[] args) {
int[] array={1,2,3,4,5};
for(int e:array) {
System. out.println(e+" ");
}
}

思考:如果System.out.println(e+' ')会输出什么?

3.2 作为函数的参数

当函数的参数是基本数据类型的时候,因为是传值拷贝,所以形参的改变不会影响实参

public static void swap(int x,int y) {
int tmp=x;
x=y;
y=tmp;
}
public static void main(String[] args) {
int a=10,b=5;
swap(a,b);
}//a和b的值不会被交换

当函数的参数是数组(引用类型)的时候,相当于把对象的地址给了形参,形参就可以修改对象

public static void swap(int[] arr) {
int tmp=arr[0];
arr[0]=arr[1];
arr[1]=tmp;
}
public static void main(String[] args) {
int[] array={10,5};
swap(array);
System. out.println(Arrays. toString(array));
}

形参arr拷贝了实参array的值,也就是arr和array指向了同一个对象

3.3 作为函数的返回值

如果我们想要接收多个同类型的返回值,可以用数组返回

比如,获取斐波那切数列的前n项

 public static int[] fib(int n) {
    int[] array=new int[n];
    array[0]=0;
    if(n==0) {
       return array;
    }
    array[1]=1;
    if(n==1) {
      return array;
     }
     for(int i=2;i<n;i++) {
       array[i]=array[i-1]+array[i-2];
     }
  return array;
}
public static void main(String[] args) {
    System.out.println(Arrays.toString(fib(5)));
}

四. Arrays提供的“金手指”

4.1 数组转字符串

这个方法其实我们已经悄咪咪使用好多遍了---Arrays.toString(数组名)

它有很多重载方法,可以接收不同数据类型的数组

4.2 数组拷贝

这里的数组拷贝并不是指将一个数组对象的地址传给其他引用变量

int[] array1=new int[10];
int[] array2=array1;
// array2指向了array1指向的对象

而是要求创建一个新的数组对象,并将原数组的值复制过去,同样Arrays类提供了这样的方法,并且适用于多种数据类型的数组拷贝

方法一:

代码示例

char[] array1={'h','e','l','l','o'};
char[] array2=Arrays.copyOf(array1,array1.length);
System.out.println(Arrays.toString(array2));

方法二:

注:from和to表示下标,是左闭右开区间

代码示例

int[] array1={1,2,3,4,5};
int[] array2=Arrays.copyOfRange(array1,3,5);
System.out.println(Arrays.toString(array2));

方法三:

src表示被拷贝的数组,srcPos表示被拷贝数组的下标起始位置,dest表示拷贝对象,destPos表示拷贝数组的下标起始位置,length指拷贝长度

与上面两个方法不同的是,方法三属于System类,并且需要自己创建空间

代码示例

拷贝array1下标为2,3的元素

int[] array1={1,2,3,4,5};
int[] array2=new int[10];
Arrays.fill(array2,10);//把array2的元素全部填充为10
System.arraycopy(array1,2,array2,0,2);
System.out.println(Arrays.toString(array2));
//输出结果
[3, 4, 10, 10, 10, 10, 10, 10, 10, 10]

4.3 查找数组中指定元素(二分查找)

如果找到了一个>=0的数,是这个元素的下标

代码示例

int[] array1={1,2,3,4,5};
System.out.println(Arrays.binarySearch(array1,3));
//输出2

4.4 数组的排序

代码示例

int[] array1={1,4,5,7};
Arrays.sort(array1);
System.out.println(Arrays.toString(array1));

五. 二维数组

请谨记这句话!!!

二维数组也是数组,只不过它的元素类型是一维数组

Java将把它体现地淋漓尽致

5.1 二维数组的创建

下面举一个创建3*3二维数组的例子

//创建且初始化

方式一:

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

方式二:

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

//只创建不初始化

方式三:

int[][] arr=new int[3][3];

5.2 二维数组的遍历

方法一:下标遍历

//定义一个3*4的数组array,元素按照0~11依次递增

int count=0;
int[][] array=new int[3][4];
for(int i=0;i<array.length;i++) {
for(int j=0;j<array[i].length;j++) {
array[i][j]=count++;
}
}

方法二:for-each

//输出数组array的每个元素

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

当然也可以方法一和二嵌套使用

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

方法三:

使用Arrays.deepToString()方法

String ret=Arrays. deepToString(array);
System. out.println(ret);

打印结果如下图

5.3 二维数组新认知

1.二维数组在定义的的时候可以省略列,但不能省略行

int[][] array1=new int[][5];//error
int[][] array2=new int[2][];//可以通过编译

2.二维数组每一行的元素个数可以不相同

int[][] array1=new int[3][];//定义一个有三个元素的二维数组

array1[0]=new int[5];//第一行元素个数为5

array1[1]=new int[]{1,2,3};//第二行元素个数为3,分别赋值为1,2,3

array1[2]=new int[4];//第三行元素个数为4
Arrays. fill(array1[2],8);//把第三行元素填充为8

System. out.println(Arrays. deepToString(array1));//打印array1

用内存图表示如下

所以打印结果是

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不 会敲代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值