昨天把数组一些基本的内容讲完了,今天扩展开讲讲
如果才接触的同学,可以先看看混个眼熟
1. 数组复制
1.1 为什么需要复制?
数组长度一旦创建就固定了。要扩容,只能新建一个更大的数组,把原数据复制过去。
1.2 方式一:手动 for 循环
int[] src = {10, 20, 30}; int[] dest = new int[5]; for (int i = 0; i < src.length; i++) { dest[i] = src[i]; } // dest = {10, 20, 30, 0, 0}1.3 方式二:System.arraycopy
System.arraycopy是java.lang.System类提供的一个静态方法,用来把一个数组的元素快速复制到另一个数组里。它在所有数组复制方式中速度最快,因为它是 JVM 内置的原生方法。
1.3.1 java.lang是属于jdk核心类库里的,为什么又说是在JVM内置的原生方法呢?
System.arraycopy的说明书(方法声明)写在 JDK 的System.java里,所以它是 JDK 类库的一部分。但它的真功夫(实现)写在 JVM 的 C++ 代码里,所以它也是 JVM 原生方法。壳在 JDK,核在 JVM。
1.3.2 什么是原生方法?
用
native关键字修饰的方法,它的实现在 JVM 底层用 C/C++ 写的,不在 Java 代码里。
1.3.3 什么是native修饰的方法?
直接调用操作系统底层的内存操作,不是一行行 Java 循环
JVM 会根据数组类型选择最优策略:整型直接一块一块复制,比循环一个个赋值快一个数量级
内部可能用 CPU 的 SIMD 指令甚至
memmove/memcpy来实现,这是最底层的内存块复制循环一个个搬砖,跟推土机直接推一整块地,效率差远了。
System.arraycopy就是推土机,手动 for 循环就是搬砖。
1.3.4 什么是静态方法?
后面会详细讲,现在可以先了解个大概
用
static修饰的方法,属于类本身,不属于任何一个对象。直接用类名调用,不用创建对象用类名直接调,不用 new 对象。
静态方法是公共设施,属于类本身,大家随时直接用。
System.arraycopy(源数组, 源起始位置, 目标数组, 目标起始位置, 复制个数); int[] src = {10, 20, 30}; int[] dest = new int[5]; System.arraycopy(src, 0, dest, 0, src.length); // 参数:源数组, 源起始位置, 目标数组, 目标起始位置, 复制个数 // dest = {10, 20, 30, 0, 0}1.4 方式三:Arrays.copyOf
Arrays.copyOf是java.util.Arrays里的一个静态方法,用来复制数组并指定新数组的长度。它内部会自动创建新数组,调用System.arraycopy,然后返回新数组。
Arrays.copyOf是最方便的数组复制方法,自动建新数组、自动调底层快速复制,扩容或复制直接用。
import java.util.Arrays; //使用时需要导包 Arrays.copyOf(源数组, 新数组长度); int[] src = {10, 20, 30}; int[] dest = Arrays.copyOf(src, 5); // 参数:源数组, 新数组长度 // dest = {10, 20, 30, 0, 0}1.5 方式四:Arrays.copyOfRange(指定范围复制)
Arrays.copyOfRange是Arrays.copyOf的兄弟方法,和copyOf一样,底层也是建新数组 + 调System.arraycopy区别在于:它可以指定复制原数组的哪一段范围,而不是只能从头开始复制。
规则:类似于数学的左闭右开 [起始,结束)
起始下标:从哪儿开始复制(包含)
结束下标:到哪儿结束(不包含)
int[] src = {10, 20, 30, 40, 50}; int[] dest = Arrays.copyOfRange(src, 1, 4); // 参数:源数组, 起始下标(包含), 结束下标(不包含) // dest = {20, 30, 40}1.6 方式五:clone
clone()是 Object 类提供的方法,用来复制一个对象。对数组来说,调用
clone()会创建一个新数组,里面的元素内容完全相同。
使用clone会引出新内容:深拷贝&浅拷贝(先简单;了解后面用到了之后细讲)
1.6.1 浅拷贝 只复制对象本身,以及对象里所有字段的「值」:
基本数据类型(int、double、boolean 等):直接复制数值,新旧对象各自独立。
引用数据类型(对象、数组等):只复制引用的内存地址,新旧对象共享同一个子对象。 → 直接结论:修改新对象的子对象内部属性,原对象会跟着同步变化。
1.6.2 深拷贝 不仅复制对象本身,还会递归复制所有层级的引用类型成员:
基本数据类型:和浅拷贝一致,复制数值,各自独立。
引用数据类型:会重新创建一个一模一样的子对象,新旧对象各自持有完全独立的子对象实例。 → 直接结论:修改新对象的任意层级属性,原对象都不会受到任何影响。
4. 数组查找
4.1 二分查找(数组必须先排序)
int[] arr = {12, 23, 55, 67, 89}; int index = Arrays.binarySearch(arr, 55); // index = 2(找到了,返回下标) int index2 = Arrays.binarySearch(arr, 100); // index2 = -6(没找到,返回负数) // 负数表示:如果插入该元素,应该在的位置的负值再减14.2 二分查找的原理
每次取中间值和目标比较,缩小一半范围
比顺序查找快得多,但前提是数组已排序。
目标:55 [12, 23, 55, 67, 89] ↑ 中间=55,命中 目标:67 [12, 23, 55, 67, 89] ↑ 中间=55,67>55,只看右边 [67, 89] ↑ 中间=67,命中5. 二维数组
数组里面的每个元素也是数组。
5.1 声明和创建
int[][] arr = new int[3][4]; // 3行4列的二维数组 int[][] arr2 = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };5.2 二维数组遍历
for (int i = 0; i < arr2.length; i++) { for (int j = 0; j < arr2[i].length; j++) { System.out.print(arr2[i][j] + " "); } System.out.println(); // 换行 } // 输出: // 1 2 3 // 4 5 6 // 7 8 9ps:扩展的有些多了,先了解反正也不吃亏