独到理解@Java数组

文章目录

  • 数组
    • 释义
    • 数组介绍
    • 数组保存数据的特性 :
    • 数组声明
    • 数组特性
    • 操作
    • 常见异常
    • 数组遍历
    • 方法调用数组的传递
    • 传值和传引用
    • 数组复制
    • 扩展练习
      • 插入复制
      • 思路 :

数组

释义

数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按无序的形式组织起来的一种形式。这些无序排列的同类数据元素的集合称为数组。
数组有一维数组和多维数组。

数组介绍

图解一维数组
独到理解@Java数组_第1张图片
什么是数组?

数组可以看做相同数据类型的数据的一个存储容器,可以对这些数据进行统一管理
在java中数组是一个源自底层的数据结构,同时也为了操作数组定义了一个java.util.Arrays类
其实不仅仅是在java中,在任何语言中,数组都是最基本的数据结构
它是一个用于存储连续的内存空间的集合,并且每个内存空间都有一个独一无二的编号 与之相对应的不连续的空间 : 叫链表

数组保存数据的特性 :

  1. 是一种引用数据类型
  2. 是一种连续的线性的数据结构
  3. 是一个数据的容器,用来存储其他数据
    数组中可以存储任意元素的,但是每一维的数据类型必须一致
  4. 数组的长度不能直接改变,也就是说数组一旦确定,里面的元素个数不能改变
    每个数组中都有一个默认的length属性,保存数组的长度(元素个数)
    如果想要更改数组的长度,需要新创建一个数组,然后把原数组中内容依次复制过去,在复制过程中可以进行删除和添加
  5. 数组下标从0开始

数组声明

  1. 静态声明
    在知道数组中每个元素的情况下,使用静态声明
    数据类型[] 变量名 = {数组元素};
		// 静态声明
		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];

数组特性

  1. 每个单独的数组中的元素类型必须一致
  2. 数组是引用数据类型,意味着占用两块空间

基本数据类型,保存的就是值
引用数据类型,保存的是内存地址的引用
数组中,用第一个元素的内存地址,作为整个数组的内存地址,然后通过内存地址偏移量能够快速找到其他元素
所以 偏移0
就是第一个元素,1就是第二个元素
这个编号 我们一般叫下标/索引 英文表示为 index
要查找某个数据的时候,通过首元素的内存地址+偏移量这种地址直寻法,查询效率极高

  1. 数据操作 : 增删改查
    结合数组特性,数组添加和删除慢,因为数组一旦确定长度不能更改,除非新建数组
    查询和更改效率较高

操作

数组[下标] 查询
数组[下标] = 值 更改

常见异常

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)即可
如何传递数组呢?

  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);
		}
	}
  1. 直接在调用方法时传递一个数组对象
    方法名( new 数据类型[]{数据} )
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]);
	}
}

数组复制

就是把某一个数组中的某些数据复制到 另外一个数组中的某个位置上去

思考 : 如何实现?
涉及到的未知点 : 某个数组,某些数据,另一个数组,某个位置
需要的必备数据 :

  1. 源数组
  2. 源数组起始位置
  3. 目标数组
  4. 目标数组起始位置
  5. 复制元素的个数

而起始位置也可以是包含,也可以是不包含
比如起始位置是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是替换式复制
如果需要在原有数组基础之上再进行添加数据,就是插入式复制

思路 :

  1. 确定一个重点,既然是插入式复制,说明数组的长度一定会更改

  2. 但是数组一旦确定,长度不能更改,如果需要更长的数组,就需要创建新的数组

  3. 如果创建的是一个新的数组的话,该方法必须要有返回值,把这个新数组返回,否则调用处是没有办法获取插入数据之后的新数组的
    并且新数组的长度 为 目标数组的长度+插入的元素个数(dest.length+length)

  4. 参数同上,还是五个
    1- 源数组
    2- 源数组起始位置
    3- 目标数组
    4- 目标数组起始位置
    5- 复制元素的个数

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

}

你可能感兴趣的:(#,数组)