数组
一丶数组概念&基本格式
(1)概念
首先,何为数组?
所谓数组,是相同元素的一个集合,在内存当中是一段连续的空间
那么这段特殊的集合有什么特点呢?
<1>数组里面的元素都是同类型的
<2>储存元素的空间是连续的
<3>每个空间都有属于自己的编号 ---- 数组的下标
(2)基本格式
数组的基本格式是:
数据类型[] 数组名 = new 数据类型[N]
在这个基本格式里面有什么需要注意的呢?
1 > 数组名命名采用小驼峰式
2 > 这里的new是一个用来申请数组空间的关键字,该关键字申请的都在堆上
3 > 后面 [ ] 的 N代表数组元素个数
二丶数组的使用
(1)数组的初始化
数组进行定义之后要进行显式初始化
<1>动态初始化
//动态初始化,例:
int[] arr1 = new int[3];
//分步写
int[] arr1;
arr1 = new int[3];
<2>静态初始化
//静态初始化,例:
int[] arr2 = new int[]{1,2,3}
//分步格式:
int[] arr2;
arr2 = new []{1,2,3}
//省略格式
int[] arr1 = {1,2,3};
这里的话有几个需要注意的点:
1.静态初始化编译器在编译的时会根据{}中元素个数来确定数组长度。
2.静态初始化的时候,{ } 里的数据类型和数组名前面的数据类型一定要一致
3.如果没有对数组初始化,其实数组里面已经有了默认值(看下面)
动态初始化的时候,数组并不是空的,里面有默认值!这一点,我们来验证一下:
首先代码截图验证:
可以看出当我们动态创建数组的时候,里面其实已经有了默认值,每种类型的默认值都不一样,具体区别如下:
这里的话,具体来总结一下,就是:
引用类型统一是:NULL
(2)数组元素的访问与遍历
对于数组来说, 其内存是一段连续的空间,空间编号从0下标开始,依次递增,所以这意味着可以通过数组的下标来访问位置,也就是说:数组支持随机访问
int[] a = {1,2,3,4,5};
System.out.println(a[3]);
//输出4
/*
所以数组下标编排是: (0,N]
N:数组元素个数
*/
这里的话,对于随机访问是知道下标打印值,那么如果知道了值打印下标呢?
我们又该怎么办呢?
public static int findDate(String[] str,String s){
int key = -1; //定义钥匙用来记录相同字符串所在下标数
for (int i = 0; i < str.length; i++) {
if(str[i] == s){
key = i;
}
}
if(key == -1){//如果没有的话返回 -1
return -1;
}
//如果有的话返回下标
return key;
}
所谓遍历,是将数组元素全部打印一遍,这里的话先介绍一下最最基本的循环打印:
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
//输出:1 2 3 4 5
PS:这里注意一下:
我对于数组打印的循环判断条件是:i < arr.length
这里说明一下,[ 数组名.length ]代表的就是数组的长度
然后这里介绍中比较简洁不容易出错的遍历方式:
for(int i : arr){
System.out.println(i);
}
这是:for - each 循环遍历
注意:这里的 i 和打印语句中的 i 要是同一个字母,打印语句 i 表示的是a [ i ] ,arr这里表示的是数组名。
好处:for - each 循环虽然功能没有for循环打印功能强大,但是可以很有效的避免数组越界和循环条件写错等 Exception 错误
三丶引用类型 – 数组
(1)内存介绍
前面也说了,数组在内存是一段连续的空间,是怎样的内存间呢?这里简略说一下:
所有的程序在没有运行的时候,就是保存在磁盘上的一段数据,但是当他们运行之后,程序的代码以及数组就需要加载到内存当中,而所谓内存是一段连续的存储空间,主要用来存储程序运行时数据的。比如:
- 程序运行时代码需要加载到内存
- 程序运行产生的中间数据要存放在内存
- 程序中的常量也要保存
- 有些数据可能需要长时间存储,而有些数据当方法运行结束后就要被销毁。
所以就要对内存严加管理,就像一房间一样,如果不好好整理:
如果好好整理:
所以JVM对内存按照功能进行了划分:
这里对这五个区域进行简略叙述
线程共享的数据区:
方法区 :用于保存已被虚拟机加载的变量,常量,类信息,即时编译器便器之后产生的代码等。方法编译的字节码就是保存在这个区域
堆 : new 出来的都在堆上,也就是引用类型的数据就保存在堆上。堆随着程序开始而创建,随着程序结束而销毁。只要堆中数据还在用,就不会销毁。
线程私有的数据区:
程序计数器 : 保存下一条执行的指令的地址
虚拟机(JVM)栈 : 每一个方法执行时都会创建一个栈帧,栈帧里面有局部变量表,操作数栈,动态链接,返回地址等其他一些信息,栈帧随着方法的运行而创建,随着方法的结束而销毁。
本地方法栈 : 保存的是Native方法的局部变量
(2)关于引用数据类型
数据类型分为基础数据类型和引用数据类型。
所谓基础数据类型,就是那四类八种,这里不做过多说明,主要介绍介绍引用数据类型。
基础数据类型的存储在栈上,而引用数据类型的存储在堆上,引用类型栈上存储的是堆上的地址。
引用数据类型目前的话认识到两种,一个是String,还有一个就是本节课的数组。
首先,先认识一下存储问题:
这里左边是代码,右边是字节码展示,现在画一下具体图示:
所有方法的执行都是从main()开始,然后在虚拟机栈创建对应栈帧,然后main一行一行执行到memory()方法,此方法在栈上创建栈帧,然后保存t a b c的值,接着到引用类型的时候,它没有这样干,而是在堆上保存了数据,然后把数据的开头地址给保存在了栈上。
这里注明一下:引用类型数组如String类型它的初始值是NULL,这个NULL和C语言中的空指针类似,都是一个无效的内存地址,不能进行读写!
四丶数组的应用
(1)作为方法返回值
一般情况下,方法的返回值只能有一个,比如整形和浮点型的数字,布尔类型的对错,如果说想要返回多个数字,那么我们可以通过数组来实现:
public static void main(String[] args) {
int[] arr = ret(5);
for (int i:arr) {
System.out.print(i + " ");//打印数组元素
}
}
public static int[] ret(int a){//接收a,确定数组长度
if(a <= 0){
return null; //确定数组元素不为空
}
int[] arr = new int[a];
for (int i = 0; i < a; i++) {
arr[i] = i + 1;//对数组进行赋值
}
return arr;
}
//最后结果是: 1 2 3 4 5
(2)作为参数传参
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
func(arr);
System.out.println("arr[0] = " + arr[0]);
}
public static int[] func(int[] a){
a[0] = 10;
System.out.println("a[0] = " + a[0]);
return a;
}
//运行结果:
a[0] = 10
arr[0] = 10
事实证明,所谓的 “引用” 本质上只是存了一个地址. Java 将数组设定成引用类型, 这样的话后续进行数组参数传参, 其实只是将数组的地址传入到函数形参中. 这样可以避免对整个数组的拷贝(数组可能比较长, 那么拷贝开销就会很大).
五丶拓展应用
(1)数组转字符串
这里的话就是单纯的记忆就好,看代码:
import java.util.Arrays
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(Arrays.toString(arr));
}
// 执行结果
[1, 2, 3, 4, 5, 6]
(2)数组拷贝
1> 深拷贝,浅拷贝
数组拷贝分为深拷贝和浅拷贝,这里分别来说一下(只针对引用类型):
浅拷贝是指不开辟新的内存空间,仅仅只是复制对象的引用地址,新旧对象都指向同一个地址,对其中一个的值得修改也会引起另外一个的改变。
例如:
int[] arr = {1,2,3,4,5,6};
int[] newArr = arr;
newArr[0] = 10;
System.out.println("newArr: " + Arrays.toString(arr));
//运行结果:
newArr: [10, 2, 3, 4, 5, 6]
深拷贝是指在内存上开辟一块新的空间,然后在上面把复制一个一模一样的对象,新旧对象不指向同一块内存,修改其中一个不影响另外一个。
这里特别标注一下,Arrays.copyOf(数组名,数组长度) 就是拷贝语句
// 深拷贝---copyOf方法在进行数组拷贝时,创建了一个新的数组
// arr和newArr引用的不是同一个数组
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
int[] newArr = Arrays.copyOf(arr, arr.length);
System.out.println("newArr: " + Arrays.toString(newArr));
}
//运行结果:
newArr: [1, 2, 3, 4, 5, 6]
2> 拷贝特定范围
可以指定拷贝几号位到几号位的元素,这里注意,拷贝的范围是:[a,b)
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
int[] newArr2 = Arrays.copyOfRange(arr, 2, 4);
System.out.println("newArr2: " + Arrays.toString(newArr2));
}
//运行结果 :
newArr2: [3, 4]
PS1:这里的范围就是 [2,4),也就是 2 ,3号下标,就是[3,4] 。
PS2:拷贝可以自己写。
六丶 二维数组
特殊的一维数组,数组里面存着数组,这里的话二维数组不太出现,所以暂时不写。