数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按无序的形式组织起来的一种形式。这些无序排列的同类数据元素的集合称为数组。
数组有一维数组和多维数组。
数组可以看做相同数据类型的数据的一个存储容器,可以对这些数据进行统一管理
在java中数组是一个源自底层的数据结构,同时也为了操作数组定义了一个java.util.Arrays类
其实不仅仅是在java中,在任何语言中,数组都是最基本的数据结构
它是一个用于存储连续的内存空间的集合,并且每个内存空间都有一个独一无二的编号 与之相对应的不连续的空间 : 叫链表
// 静态声明
int[] arr1 = { 1, 2, 3 };
byte[] arr2 = { 2, 2, 3 };
boolean[] arr3 = { false, false, false };
char[] arrr = { 'a', '2', 'c' };
System.out.println(arr1.length);
// []也可以放在变量名后,但是不建议这样写
int arr5[] = { 1, 2, 2, 1 };
int i = 5;
int[] is = { 1, 2, 3, 4, 54 };
int[][] iss = { { 1, 2, 3 }, { 2, 3 }, { 5, 4, 3 } };
int[][][] isss = { { { 1, 2, 3 }, { 2 } }, {}, {} };
2 动态声明
就是预先不知道要保存的数据是什么值,但是我们需要先把空间划分好,需要使用动态声明,并且会使用对应的默认值去占位
整数 0
小数 0.0
布尔 false
字符 \u0000
引用 null
语法结构 :
数据类型[] 变量名 = new 数据类型[长度];
// 创建一个 int一维数组,有10个元素,默认值为0
// 动态声明
int[] arr4 = new int[10];
基本数据类型,保存的就是值
引用数据类型,保存的是内存地址的引用
数组中,用第一个元素的内存地址,作为整个数组的内存地址,然后通过内存地址偏移量能够快速找到其他元素
所以 偏移0
就是第一个元素,1就是第二个元素
这个编号 我们一般叫下标/索引 英文表示为 index
要查找某个数据的时候,通过首元素的内存地址+偏移量这种地址直寻法,查询效率极高
数组[下标] 查询
数组[下标] = 值 更改
public static void main(String[] args) {
int[] arr = { 97, 98, 99 };
// 第一个元素
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
arr[2] = 99;
System.out.println(arr[arr.length - 1]);
// Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
// 报错后会终止生命周期,停止执行
// 数组越界异常
// System.out.println(arr[3]);
上图中,数组元素有3个,下标分为 0,1,2
但是我去获取下标为3的数据,导致 下标超过范围,报错
报错后会终止程序生命周期执行,后面代码不会执行,直接结束程序
通过通过数组的length属性 作为终止条件进行数组的遍历
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
增强for循环 foreach
// 增强for循环
for (int i : arr) {
System.out.println(i);
}
把数组中每一个元素都赋值给 前面的变量
然后执行操作这个变量即可(不需要下标了)
数据类型 需要和 数组中元素类型一致
我们之前调用方法时传递过int i 等基本类型,在调用时 直接 m1(1)即可
如何传递数组呢?
int[] is = { 1, 2, 3, 4, };
println(is);
// println方法的重载
public static void println(int[] is) {
// TODO Auto-generated method stub
for (int i : is) {
System.out.println(i);
}
}
System.out.println("---");
println(123);
// 123
println(new int[] { 1, 2, 3, 4 });
// println方法的重载
public static void println(int[] is) {
// TODO Auto-generated method stub
for (int i : is) {
System.out.println(i);
}
}
public static void println(int i) {
System.out.println(i);
}
注意 :
超出下标异常
public class Array_04 {
public static void main(String[] args) {
System.out.println(args.length);
for (String string : args) {
System.out.println(string);
}
// 0
// 没有堆内存数组对象
int[] arr1 = null;
// 有堆内存数组对象,但是里面 没有数据
int[] arr2 = new int[0];
System.out.println(arr1);
// null
System.out.println(arr2);
// [I@15db9742
// 会报空指针异常 Exception in thread "main" java.lang.NullPointerException
// System.out.println(arr1[0]);
// System.out.println(arr1.length);
// 超出下标异常 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
// System.out.println(arr2[0]);
}
}
传值 : 基本数据类型传递
传引用 : 引用数据类型传递
总结 :
传值 : 对方把数据更改后,和调用处无关,没有任何影响
传引用 : 对方把数据更改后,调用处的数据也会随之更改
public class Array_05 {
public static void main(String[] args) {
int i = 10;
m1(i);
System.out.println(i);
// 运行结果
// 20
// 10
int[] is = { 1, 2 };
m2(is);
System.out.println(is[0]);
// 运行结果
// 100
// 100
}
public static void m1(int i) {
i *= 2;
System.out.println(i);
}
public static void m2(int[] isa) {
isa[0] = 100;
System.out.println(isa[0]);
}
}
就是把某一个数组中的某些数据复制到 另外一个数组中的某个位置上去
思考 : 如何实现?
涉及到的未知点 : 某个数组,某些数据,另一个数组,某个位置
需要的必备数据 :
而起始位置也可以是包含,也可以是不包含
比如起始位置是2 ,取三个数据
包含 就是 : 2,3,4 下标对应的数据
不包含 就是 : 3,4,5 下标对应的数据
System类中提供了一个复制数组的方法
arraycopy中的起始位置是包含关系
由于是替换式复制,并且传递的是引用类型
所以不需要返回值,调用处的数组 也会更改
public class Array_06 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] src = { 2, 3, 4, 5, 6, 7, 8 };
int[] dest = { 11, 12, 13, 14, 15, 16, 17, 18 };
// System.arraycopy(src, 2, dest, 3, 3);
arraycopy(src, 2, dest, 3, 3);
for (int i : dest) {
System.out.println(i);
}
// 11
// 12
// 13
// 4
// 5
// 6
// 17
// 18
}
public static void arraycopy(int[] src, int srcPos, int[] dest, int destPos, int length) {
// 复制个数,意味着循环次数
// 把src的起始值上的数据,赋值给dest数组起始值对应的位置
// 两个起始值依次递增
for (int i = 0; i < length; i++) {
// destPos++;
// srcPos++;
dest[destPos] = src[srcPos];
// 包含起始值 就先赋值 再递增
// 不包含起始值 就先递增 再赋值
destPos++;
srcPos++;
}
}
}
上面的arraycopy是替换式复制
如果需要在原有数组基础之上再进行添加数据,就是插入式复制
确定一个重点,既然是插入式复制,说明数组的长度一定会更改
但是数组一旦确定,长度不能更改,如果需要更长的数组,就需要创建新的数组
如果创建的是一个新的数组的话,该方法必须要有返回值,把这个新数组返回,否则调用处是没有办法获取插入数据之后的新数组的
并且新数组的长度 为 目标数组的长度+插入的元素个数(dest.length+length)
参数同上,还是五个
1- 源数组
2- 源数组起始位置
3- 目标数组
4- 目标数组起始位置
5- 复制元素的个数
如何实现功能
1_ 把目标数组中起始位置之前的元素先放到新数组中
2_ 再把源数组中起始位置开始到复制的个数之间的数据,放入新数组中
3_ 最后把目标数组中起始位置之后的元素放入新数组中
4_ 最终返回新数组
[实现] :
public class Array_07 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] src = { 2, 3, 4, 5, 6, 7, 8 };
int[] dest = { 11, 12, 13, 14, 15, 16, 17, 18 };
int[] newDest = arraycopy(src, 2, dest, 3, 3);
for (int i : newDest) {
System.out.println(i);
}
}
public static int[] arraycopy(int[] src, int srcPos, int[] dest, int destPos, int length) {
// 1 确定一个重点,既然是插入式复制,说明数组的长度一定会更改
// 2 但是数组一旦确定,长度不能更改,如果需要更长的数组,就需要创建新的数组
// 3 如果创建的是一个新的数组的话,该方法必须要有返回值,把这个新数组返回,否则调用处是没有办法获取插入数据之后的新数组的
// 并且新数组的长度 为 目标数组的长度+插入的元素个数(dest.length+length)
int[] newDest = new int[dest.length + length];
// 1 把目标数组中起始位置之前的元素先放到新数组中
for (int i = 0; i <= destPos; i++) {
newDest[i] = dest[i];
}
// 2 再把源数组中起始位置开始到复制的个数之间的数据,放入新数组中
// 因为我们后面还需要用到destPos,所以我们不能更改destPos这个值
// 但是我们循环放入需要 ++,所以把destPos的值,赋值给一个变量
// 然后使用这个变量++,既能完成放入操作,还不会影响destPos的值
int index = destPos;
for (int i = srcPos; i < srcPos + length; i++) {
index++;
newDest[index] = src[i];
}
// 3 最后把目标数组中起始位置之后的元素放入新数组中
for (int i = destPos + 1; i < dest.length; i++) {
index++;
newDest[index] = dest[i];
}
// 4 最终返回新数组
return newDest;
}
}