文章目录
- 什么是数组
- 创建数组
- 数组的使用
- 遍历数组
- 数组作为方法的参数
- 这时候,我们来讲讲前面文章留下来的问题(使用方法,来交换两个变量的值)
- 数组作为方法的返回值
- 数组练习
- 模拟实现 toString 函数
- 找数组中的最大元素
- 查找数组中指定元素(顺序查找),返回它的下标
- 查找数组中指定元素(二分查找),返回其下标
- 其实在Java中有一个工具,就是二分查找,意味这我们没必要像上面一样去写一个二分查找的方法
- 二分查找对于有序数列的效率是非常高的,你想想取一个中间下标的值
- 检查数组的有序性(升序:从左往右,从小到大)
- 数组排序(冒泡排序)
- 经过上面的讲解,至少你对冒泡有一定的了解,很遗憾,我想告诉你,我们辛辛苦苦做出的冒泡排序,在Java是有函数可以做到的(但并不意味我们刚才的努力都是白费,万一面试官,让你模拟实现一个冒泡排序呢?)对吧? 废话不多说,我们来看看这哥函数是什么?
- 我们在顺便拓展一下 Arrays 的其它功能
- 根据图 43,发现 Arrays。fill 的参数,还可以写两个
- 数组逆序
- 数组数字排列
- 数组拷贝
- 在 一维数组的最后我们在讲一个概念, 深拷贝 和 前拷贝
- 二维数组
- 本文结束。
什么是数组
数组本质上就是让我们能 "批量" 创建相同类型的变量
也可以说是存储一组相同数据类型的数据的集合
举个例子,我现在要创建一个整形数组
int[] array
int[] 就是相当于是一个类型(整形数组类型),
而 array 就相当于 该类型的变量
总得来说 就是我创建一个变量,它的类型是整形数组类型
再来看看这个 int a =10; a是变量,类型是int,且只能存储一个值。
而 int[] array,array是变量,类型是int[],它能存储一组相同类型的数据(能存储类型相同的数据,个数不限,取决你想放多少个类型相同的数据)
例: int[] array ={1,2,3,4,5,6}
注意事项: 在 Java 中, 数组中包含的变量必须是 相同类型
创建数组
1.静态初始化
基本语法
数据类型[] 数组名称 = { 初始化数据 };
程序实例
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array ={1,2,3,4,5,6};
//数据类型[]+数组名称 = { 初始化数据 }; 如果后面已将数组初始化元素值了,其中[]里不能加任何数字,这是Java创建数组时,静态初始化的一个特点.
}
}
既然创造出数组,就必然有它存在的价值,那么数组的好处是什么?
如果我们没有数据的支持,我们想要定义 6 个整形会变得巨繁琐,反正我学了数组,像例子这种呆头呆脑的写就没用过了。
例
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int f = 6;
}
}
而且对数组有一定了解的人,都知道数组的每个元素都有一个下标(从0开始),方便去寻找寻找元素.
而且是所有元素都存储在一块连续的空间上,所以从内存上看,所有元素都是紧邻(图1)</font>
图1
注意事项: 静态初始化的时候, 数组元素个数和初始化数据的格式是一致的(依据初始化元素的数量,来决定数组的大小)
2.动态初始化
基本语法
数据类型[] 数组名称 = new 数据类型 [] { 初始化数据 };
代码实例
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = new int[]{1,2,3,4,5,6};//其表达意思与作用与第一种方法相同,就是创建一个数组,并初始化数据
// 两个[]中,都不能有数字的存在
// new 是java中的一个关键字,其作用是实例化一个对象
// 意味着 在Java 数组其实是一个对象。对象具体是什么,后面再降
// (注意,并不是前面有new,数组才是一个对象,而是数组本来是就是一个对象)
//先这么认为,数组就是一款网页游戏,游戏没打开之前,它只是一个图标,而new的作用就是加载这游戏成为实体,就是运行起来。
}
}
第三种创建数组的方法
基本语法:
类型[] 数组名 = new 类型[元素个数];
就是第二种方法中,删除了初始化数据,前者的[]中是不能有数字的存在,后者的[]可以有具体的数字来表示数组元素的个数
代码实例
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = new int[6];
// 该写法表达的意思是 现在有一个数组,长度为6,注意数组的元素,并没有对其初始化,在Java中,默认6个元素都是0
// 等价于 int[] array={0,0,0,0,0,0},
}
}
总结
一套讲解下来,你会发现在Java中 是这么来表达一个数组:int[] array
其实数组也可以写成
int arr[] = {1, 2, 3};
和 C 语言一样. 但是我们还是更推荐写成 int[] arr 的形式. int和 [] 是一个整体.,因此,其实在Java中数组的写法更为准确。
但是不能像C语言一样这样写 int array[10] = {0};
我们前面也看到了,在创建一个数组时,[]里是不能有具体数字的存在,除了第三种方法,其它的,一律不行。
数组的使用
代码实例1(获取数组的长度)
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
// 注意在Java是没有sizeof这种东西的,那么有人可能会问,在数组元素个数未知的情况下,我们该如何获得数组元素个数?
// 方法 : 数组名.length 这样写Java就会自动获取数组的长度(元素个数)。
System.out.println(array.length);
}// 图2
}
图2
代码实例2 (读取数组中某个元素)
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array={1,2,3,4,5};
// 下标从0开始,4的下标就是 3
// 访问方法: 数组名[下标]
System.out.println(array[3]);
}// 图 3
}
图3
代码实例3(数组越界问题)
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array={1,2,3,4,5};
// 下标从0开始递增,也就是正数下标,不存在所谓的负数下标
System.out.println(array[-1]);// 我们将访问元素的下标写成负数,让我们来看看效果如何。
}// 图 4
}
图4
续代码3
还有一种情况:元素只有 5 个,下标最高为4,你却要访问第6个元素,也就是下标为 5 的元素
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array={1,2,3,4,5};
System.out.println(array[5]);// 现在我们将访问元素的下标写成5,让我们来看看效果如何。
}// 图 5
}
图 5
代码实例4(改变数组某元素的值)
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array={1,2,3,4,5};
// 改变 元素3 的值,也就是改变数组中下标为2的元素值
// 方法 数组名[下标] = 新值;
array[2]=8;
System.out.println(array[2]);// 让我们来看看效果如何。
}// 图 6
}
图6
注意事项
1. 使用 arr.length 能够获取到数组的长度. . 这个操作为成员访问操作符. 后面在面向对象中会经常用到.
2. 使用 [ ] 按下标取数组元素. 需要注意, 下标从 0 开始计数
3. 使用 [ ] 操作既能读取数据, 也能修改数据.
4. 下标访问操作不能超出有效范围 [0, length - 1] , 如果超出有效范围, 会出现下标越界异常
遍历数组
代码实例1(for循环 - 遍历打印数组元素)
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array={1,2,3,4,5};
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]+" ");
// println 是因为它自带换行,输出结果是竖着的,不好看。
// 所以我使用的是 print 打印不换行
}//图 7
}
}
图7
代码实例2(增加for循环 - foreach - 图 8)
foreach 基本语法:
for(定义一个 与数组元素类型 相同的变量 : 数组名)
什么意思呢?
foreach(增加for循环), 数组名部分,表示的意思 遍历访问数组的元素
将访问的元素赋给 冒号前面 定义的 与数组元素类型相同 的变量
我们只需要 将该变量每次得到的元素值,打印
就能做到不依靠元素下标,遍历打印数组所有元素
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
for (int x:array) {
System.out.print(x+" ");
}
}//图9
}
图 8
图9
那么 for 和 foreach 两者有什么区别?
最大的区别在于,for是可以拿到元素下标,而foreach拿不到元素下标
for循环用到的地方很多,但是foreach呢?
当我们只需要元素的值时,就是使用foreach,
当我们还需要元素的下标时,就用for。
for-each 是 for 循环的另外一种使用方式. 能够更方便的完成对数组的遍历. 可以避免循环条件和更新语句写错
代码实例3(借助Java的操作数组的工具类 Arrays 图10)
toString 将当前的数组元素,转换成字符串形式,并将其返回(也可以说 将参数的数组,以字符串形式进行输出)
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
String str = Arrays.toString(array);// 创建一个字符串变量来接收它的返回值
System.out.println(str);// 图 11
// 因为 Arrays.toString 是有返回值的,所以可以直接输出
System.out.println(Arrays.toString(array));//图12
}
}
图10
图11
图12
数组作为方法的参数
首先我们需要搞懂几样东西
看代码
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array ={1,2,3,4,5};
int a = 10;
}
}
array 和 a 都是局部变量,在栈上开辟空间
那么问题来了,a的空间里存的是 10
而数组后面的一大坨数据,放在哪里?
和 10 一样放在栈上?
然而并不是,还记得前面说到 数组是一个对象吗?
对象是存放在 堆上的.
在这里我们就需要介绍一些东西,图13
图13(这图没截好,下半部分少了东西,不过无伤大雅)
引用很像指针,最大的区别就是不能对 引用 解引用,可以对指针解引用,因为在Java没有传址的概念的,其他功能类似,但时两个东西,不是同一个东西,不能混淆在一起。
有个问题就诞生了,指针有空指针,那引用有空引用(引用里面存的地址是空的)吗?
答案是有的,只不过跟C语言不同,C是大写NULL,Java是小写null
但是请注意,举这个例子只是为了让你理解引用是一个什么东西,顺便区别两者。
C的东西只是辅助你理解Java,尽量学Java的时候,抛开C,两者(引用和指针)有很多不同地方。
代码实例
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] array1 = null;
//当一个引用被赋予null,就代表这该引用不指向任何对象
// 既然该引用不指向任何对象,那么我这样写代码会怎么样?
System.out.println(array1.length);
// 求一个空引用所指向的对象的长度,但是空引用不会指向任何一个对象的
// 何来的长度?那么代码运行的效果如何?
图14,由图可知该写法是错误的,另外引用被指为null,堆上是不会分配内存给它的
// 意思是你都不创建任何对象,你还想拿我空间,疯了?
System.out.println(array1[0]);
// 这个写法毫无疑问也是错的,没对象,你怎么访问对象里的东西?
来看看图 15
}
}
图14
图15
现在我们来尝试用方法来遍历打印数组
代码如下
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
print(array);
}
public static void print(int[] array){
for (int x:array) {
System.out.print(x+" ");
}
System.out.println();// 换行
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]+" ");
}
}// 效果见图16
}//让我们通过 图17 来说明
图16
图17
举个例子,你玩游戏DNF,有仓库对吧,你会设密码对吧?要不然被盗号,就算找回来,里面的东西也不见了。
只要别人知道你的密码,他不就可以拿里面的东西了吗?
按引用传递,就相当于你找朋友刷东西,把密码告诉他,这样他才能打开你的仓库,在里面拿或者取 装备和材料,不然他怎么刷的动?
实践题
下面两题先思考答案,再去看结果附图,这样你们才能对引用的理解进一步加深
下面两题的输出结果是什么?
题目一
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
func1(array);
System.out.println(Arrays.toString(array));// 图 18
}
public static void func1(int[] array){
array = new int[]{11,2,13,4,51};
}
}
题目2
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
func2(array);
System.out.println(Arrays.toString(array));
}
public static void func2(int[] array){
array[0] = 99;
}
}//图19
图 18
图19
##题目路2的两个array等价于下面的程序 array 和 array2
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] array2 = array;
这代表 array2 这个引用 指向 引用array指向 的对象
array 和 array2 指向的是同一个对象
}
}
图解(图20)
还有一个问题值得讨论:一个引用 是否 能同时 指向 多个对象?
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array1 = new int[]{1,2,3,4,5};
array1 = new int[10];
array1 = new int[2];
array1 = new int[4];
}
}
答案是不能,如果前面认真看了,就该知道此时的array1,存储的地址,已经被改变(array是一个局部变量,意味着存的值是可以被改变的),现在存的是 new int[4] 的这个对象的地址, 而不是说,存几个对象的地址。
结论
一个引用只能指向一个对象(一个引用只能保存一个对象的地址)
另外注意
写了这么多,有没有发现我们写的引用,都是在栈上
那么 引用 就一定在栈上吗?
答案是不一定的,因为一个变量在不在栈上,是你变量的性质决定的
如果你的引用是一个局部变量,那就一定在栈上
实例成员变量那就不一定了。(先告诉你们有这个概念,等后面讲到成员变量时,再说)
局部变量的引用保存在栈上, new 出的对象保存在堆上.
堆的空间非常大, 栈的空间比较小.
因为 堆 是整个 JVM 共享一个,
而 栈 每个线程具有一份 (一个 Java 程序中可能存在多个栈)
这时候,我们来讲讲前面文章留下来的问题(使用方法,来交换两个变量的值)
因为Java中是取不到栈上变量的地址的,也就意味不能传址,所以无法按照C一样去实现
但是今天我们学了数组,我们用数组的方式来解决问题
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
//int a = 10;
//int b = 20;
// 我们将其放在一个数组里,进行交换
int[] array = {10,20};
swap(array);
System.out.println(Arrays.toString(array));
}
public static void swap(int[] array){
int tmp = array[0];
array[0] = array[1];
array[1] = tmp;
}
}// 图21,这只是一种取巧的方式来进行交换,等到我讲到类和对象的那个时候,会再讲几种方法
图21
数组作为方法的返回值
代码示例: 写一个方法, 将数组中的每个元素都 * 2
先来写没有返回值的(将原来的数组元素都扩大2倍)
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array={1,2,3,4,5};
func(array);
System.out.println(Arrays.toString(array));
}
public static void func(int[] array){
for (int i = 0; i < array.length; i++) {
array[i]=2*array[i];
}
}//图22
}// 图解(图23)
图 22
图23
现在我们来写有返回值的(不在原来的数组上扩大2倍,保护原来的数组不被破坏)
代码入下
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array={1,2,3,4,5};
int[] ret = func(array);//建立数组类型(对象)变量来接收
System.out.println(Arrays.toString(ret));
System.out.println(Arrays.toString(array));
}
public static int[] func(int[] array){
int[] ret = new int[array.length];
for (int i = 0; i < array.length; i++) {
ret[i]=2*array[i];
}
return ret;//这里返回是 新建对象(数组),在堆上的地址
}//图24
}// 图解(图25)
图24
图25
这样的话就不会破坏原有数组了.
另外由于数组是引用类型, 返回的时候只是将这个数组的首地址返回给函数调用者, 没有拷贝数组内容, 从而比较高效.
有的人可能会说,方法调用完的时候,不是会销毁栈上的空间,我们这样将地址带回来,不会存在一些问题吗?
请注意 new 的对象,都是存在堆上的,而不是栈上。这里的返回的是将堆上对象的地址(注意返回类型int[]),而不是局部变量ret,所以不会出现问题。
数组练习
模拟实现 toString 函数
toString 将当前的数组元素,转换成字符串形式,并将其返回(也可以说 将参数的数组,以字符串形式进行输出) 图12
代码如下
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array={1,2,3,4,5};
String str = myToString(array);
System.out.println(str);
}
public static String myToString(int[] array){
if(array == null){
return "null";// 判断是否形参接收的地址,是否为空指针
}
String str = "[";
for (int i = 0; i < array.length; i++) {
str+=array[i];// 用了一开始拼接输出方式,字符串开头后面用+拼接,使其成为一个字符串
if(i< array.length-1){// array.length-1,因为最后一个元素不用加 ,号
str+=",";
}
}
str+="]";
return str;
}
}// 图26
图26
找数组中的最大元素
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {12,8,1,2,10};
System.out.println(maxNum(array));
}
public static int maxNum(int[] array){
if(array==null){
return -1;// 假设 -1 意味着array存储的是空指针
}
if(0 == array.length){
return -2;// 假设 -2 意味着 array 没有元素(int[] array = {})
// 故 数组长度为0(元素个数为0).
}
int max = array[0];
for (int i = 1; i < array.length; i++) {
if(max<array[i]){
max = array[i];
}
}
return max;
}// 图 27
}
图27
查找数组中指定元素(顺序查找),返回它的下标
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,23,213,67,123};
System.out.println(find(array,23));
}
public static int find(int[] array,int y){
for (int i = 0; i < array.length; i++) {
if (array[i] == y){
return i;
}
}
return -1;// 表示没找到,因为数组的下标不可能存在负数
}
}
查找数组中指定元素(二分查找),返回其下标
针对有序数组, 可以使用更高效的二分查找
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
System.out.println(find(array,4));
}
public static int find(int[] array,int y){
int right = array.length-1;// 右下标
int left = 0;// 左下标
int mid = 0;
while(right>=left){
mid=(right+left)/2;
if(array[mid]>y){
right--;
}
else if(array[mid]<y){
left++;
}else{
return mid;
}
}
return -1;//表示没找到
}
}// 图28
图释( 图29)
图28
图29
其实在Java中有一个工具,就是二分查找,意味这我们没必要像上面一样去写一个二分查找的方法
写法: Arrays.binarySearch(数组名,想查找的元素)
代码如下
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
System.out.println(Arrays.binarySearch(array,2));// 图30
// 如果找一个不存在的数呢?
System.out.println(Arrays.binarySearch(array,6));// 图 31
// 有人会好奇,为什么是-6?
// 左键点击 binarySearch,ctrl + 左键点击
// 再点击 binarySearch,ctrl + 左键点击
你会看到 图 32
// -(low+1),意思是最后low的位置下标+1,取负
// 因为我们要找的数,比有序数组里所有元素都要大,所以最后low的位置应该最后一个元素的位置
// 也就是下标为 5 的位置,对其加一取负,所以输出的值是-6
// 另外注意,下标是没有负数的,别以为这-6 是一个元素的下标,不是的啊。
// 只是告诉你找不到。
// 下标不可能为负,你给我个负数,不是找不到是什么
}
}
图30
图31
图32
二分查找对于有序数列的效率是非常高的,你想想取一个中间下标的值
不管你要找的数,是大于或者小于这个值,都意味着有一边的数据直接pass。
图33
检查数组的有序性(升序:从左往右,从小到大)
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,10,5,6};
System.out.println(isSorted(array));
}
public static boolean isSorted(int[] array){
for (int i = 0; i < array.length-1; i++) {
// 注意 array.length-1,为什么减一呢?因为你想想看,假设有6个元素(array.length == 6)
// 但是下标最大为 5 == array.length-1,因为有小于号,所以这个减一,可以被省略,那为什么还要减一?
// 注意我们的if条件(array[i]>array[i+1]),当循环元素到下标为5的元素(最后一个元素),就会有问题了
// array[i+1] == array[6] == 第七个元素,第七个元素有没有?没有。那我们访问的话会产生数组越界访问错误
// 图35
if(array[i]>array[i+1]){// 左边的元素比右边的元素大,那么就不满足升序,称为乱序
return false;// 乱序
}
}
return true;// 有序
}
}// 图34
图34
图35
数组排序(冒泡排序)
升序
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {9,5,2,7};// 我们假设一个整形数组
bubbleSort(array);
// 调用我们的自定义的冒泡的方法,把我们的数组名(引用)扔过去,按引用传递
System.out.println(Arrays.toString(array));// 偷个油,不用循环打印,将其转换成字符串输出
}
public static void bubbleSort(int[] array){// 创建一个 数组变量,来接收传参
// 理一下思绪,首先假设我定义一个 i 来表示元素下标,通过引用(数组名) 和 [] 来访问下标所对应的函数。
//既然要排升序,肯定要比较大小,不可能自己跟自己比吧?要跟它后面的元素比,比它大,相互交换位置,反则什么都不做(升序)。
// 但是不可能只比一次,我这里有四个元素,至少要比3次,才能完成把最大的数放在最后。
// 有的人可能会说,不是有4个元素吗?不应该是4次,记住我们2个一比,每次都是前面比后面,
// 到了倒数第二个元素,它是最后的元素相比较,比较完了,那么最后一个元素已经最大的了,还有比较的意义吗?没有!
//而且这是,一趟,还要比较3趟。我们第一趟比较只是把最大的数,放到了最后,其它三个数,还不知是否是升序
图 36
// 现在我们正式开始
for (int i = 0; i < array.length; i++) {// 有4个元素,需要比 3 趟
// 根据 图36 最后的结论,表示有可能,在未来完成某一趟比较之后,后面的数值已经有序,那我们没有比较的必要
// 所以 我们在这里定义一个 flag = 1,再在 交换程序中 写一个flag=0;
// 什么意思呢? 就是你只要交换了两元素的位置,说明这一趟,数组肯定不是有序的,你都交换了,还有序?
// 如果 这一趟下来flag还是等于1,我们结束整个排序,为什么?因为根据图36来看,冒泡排序的比较模式 是交换式比较,
// 1 和 2 一比,比完; 2 和 3一比,也就是如果flag等于1,说明后面没有发生交换
// 后面的每一位都比自身前一位都要大(比前面的位,早在之前就给你换了)
// 说明升序排列完成
int flag =1;// 假设这一趟是有序的
// 每一趟的比较过程
for (int j = 0; j < array.length-1-i; j++) {// 减一是为了防止越界,这个应该都懂
// 至于减 i,想想看,我们每一趟比较,把最大的数往后排,小的数往前挪,作为下一次标胶的起点
// 再加上这一趟比下来,可以说一个数跟其他都比较了,只有全比较了,我们才能知道最大数究竟是谁?
// 得出了最大数,放在最后,那还有比较的必要,再去比,就是疯了。(都打过一架了,还输了,你还找别人去单挑,不是疯了,就是zz)
// 所以每比完一趟,我们比较次数就减一,
// 第一次,i==0,是因为倒数2个大值,还需要比较,你再减一个1,还比什么。搞黑手?还没比,就把最后的一位数,放在最后一位
// 万一倒数第二位,比最后的数,要大呢?,这不是搞黑幕,是搞什么?嗯?
if(array[j]>array[j+1]){
// 升序,前者比后者小,只要前者比后者大,就意味着需要交换位置
int tmp = array[j];
array[j] = array[j+1];
array[j+1]=tmp;
// 假设
// 2 1
// tmp = 2 == array[0] ; array[0] = array[1] == 1,此时array[0]已经被改变
// array [1] = tmp ==2;
// 即 数组元素顺序 为 1,2
flag = 0;// 都交换了,那么肯定是不是有序的
}
}
if(1== flag){
break;//如果flag等于1,说明后面的每一位都比自身前一位都要大,那就说明没有比较的意义,同时意味着排序完成,跳出循环。
}
}
来看看看结果 图 37
}
}
图36
图37
经过上面的讲解,至少你对冒泡有一定的了解,很遗憾,我想告诉你,我们辛辛苦苦做出的冒泡排序,在Java是有函数可以做到的(但并不意味我们刚才的努力都是白费,万一面试官,让你模拟实现一个冒泡排序呢?)对吧? 废话不多说,我们来看看这哥函数是什么?
Arrays.sort
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {9,5,2,7};
Arrays.sort(array);// 是借助Java工具Arrays.sort来完成的,图 39
System.out.println(Arrays.toString(array));// 图38
}
}
图38
图39
我们在顺便拓展一下 Arrays 的其它功能
Arrays.fill
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = new int[10];// 此时数组默认10个元素都是0,图40
Arrays.fill(array,99);// fill 是 填的意思,图 42
System.out.println(Arrays.toString(array));// 效果见 图 41
}
}
图 40
图41
图42
根据图 43,发现 Arrays。fill 的参数,还可以写两个
图43
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = new int[10];// 此时数组默认10个元素都是0,图40
Arrays.fill(array,0,5,99);// 意思将下标为0的元素到下标为4的元素全部置为0
// 至于为什么不是下标为5,是因为 该范围是 左闭右开 [0,5) == 0=<下标 && 下标<5
System.out.println(Arrays.toString(array));// 效果见 图 44
}
}
图44
数组逆序
假设数组元素顺序为 1,2,3,4,5(并不一定是有序,只是为了举例方便)
结果为 5,4,3,2,1
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
echange(array);
System.out.println(Arrays.toString(array));// 图 45
}
public static void echange(int[] array){
int left =0;// 左下标
int right = array.length-1;// 右下标
while(left<right){
// 就是两边的数据对着换
// 当left 和 right 所指向的下标相遇是,也就同一个元素
// 循环终止
int tmp = array[left];
array[left]=array[right];
array[right]= tmp;
right--;
left++;
}
}
}
图45
数组数字排列
给定一个整型数组, 将所有的偶数放在前半部分, 将所有的奇数放在数组后半部分
例如
{1, 2, 3, 4}
调整后得到
{4, 2, 3, 1}
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4};
transform(array);
System.out.println(Arrays.toString(array));// 图 46
}
public static void transform(int[] array){
int left = 0;
int right = array.length-1;
while(left<right){
while(left<right && array[left]%2 == 0){
// 找奇数(%2 !=0),找到了,跳出循环
left++;
// left 是不能一直加加的,如果没有 left<right 这个限制条件,在交换完成(left和right相遇),该下标还会一直 ++。
// 甚至可能会越界(全偶数),因为(小)循环体执行完了,才会去判断(大)循环条件,
}
while(left<right && array[right]%2 != 0 ){
// 找偶数(%2 == 0),找到了,跳出循环
right--;// 这个与left同理
}
int tmp = array[left];
array[left] = array[right];
array[right] = tmp;
}
}
}
图46
数组拷贝
拷贝方法1 循环拷贝
假设你要拷贝的数组 是 int[] array = {1,2,3,4,5}
那么我就需要 创一个相同类型,元素个数相同的 数组变量array2来接收(int[] array2 [array.length])
通过 对应的下标进行复制拷贝
代码如下
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array={1,2,3,4,5};
int[] array2 = copyOf(array);// 是方法中 引用ret存储值的一份拷贝,能够访问 ret 指向的堆上的对象
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(array2));// 图 47
}
public static int[] copyOf(int[] array){
int[] ret = new int[array.length];//创一个相同类型,元素个数相同的 数组变量array2来接收 array 的元素
for (int i = 0; i < array.length; i++) {
ret[i] = array[i];//通过 对应的下标进行复制拷贝
}
return ret;// 将对象的地址转过去
}
}// 图 48
图47
图48
方法2
利用Java提供的函数: Arrays.copyOf
图49
图 49
代码如下
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] array2 = Arrays.copyOf(array,array.length);
System.out.println(Arrays.toString(array2));// 图 50
// 既然能规定 拷贝数量,我输入拷贝数量超过,源数组array的元素个数呢?
int[] array3 = Arrays.copyOf(array,array.length*2);
System.out.println(Arrays.toString(array3));// 图 51,由图可知类似扩容
}// 但是注意一点,本质是copy明白嘛? 这扩容出来的数组,跟元素组,不是同一个数组
左键点击copyOf +Ctrl+左键),来看看得到的 图52 ,由图52,还可以得知 如果我们想"扩容",输入的倍数必须整数,因为它的长度类型规定为整形
}
图50
图51
图52
方法3
不知道你们注意到没? 图52中的一个程序语句
图 53
图中所圈部分,就是我们拷贝数组的第三种方法
图53
但是我们先来看第四种方法,为第三种方法做铺垫
Arrays.copyOfRange()
图54
代码如下
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] ret = Arrays.copyOfRange(array,0,4);
System.out.println(Arrays.toString(ret));//图 55
}
}
图 55
好,现在我们来看看 第三方法
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] array2 = new int[array.length];
System.arraycopy(array,0,array2,0,array.length);
// 跟用for循环进行拷贝时一个道理,根据对应的下标进行赋值拷贝
System.out.println(Arrays.toString(array2));
// 图 56
}
}
图56
拓展
图 57
注意图中所圈部分,怎么进我就不再教了,忘了就往上翻。
我来提示你,System.arraycopy是不是没有实现过程,只是一个类似函数声明一样,放在那里?
那它是怎么实现数组拷贝的呢?
这就跟我们所圈起来的native(本地的)有关,
所有被 native 所修饰的方法,方法的实现过程,都已经被 C/C++ 实现了。
我们是看不到运行过程的,这样写的最大好处就是 速度快,效率高
方法 5 数组克隆
基本语法:
数组名.clone();
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] array2 = array.clone();// 类似 副本,不是游戏副本啊!
// 可以理解为是一份资料的备份
System.out.println(Arrays.toString(array2));
}// 图58
}
图58
在 一维数组的最后我们在讲一个概念, 深拷贝 和 前拷贝
图 59
&ensp;
二维数组
二维数组的创建
基本语法1
数据类型[][] 数组名 = { 初始化数据 }
基本语法2
数据类型[][] 数组名 = new 数据类型[][]{ 初始化数据 }
基本语法3
数据类型[][] 数组名 = new 数据类型[行数][列数]
跟一维数组几乎一样,只要初始化了数据,你的[][]里就不能有数字的存在
代码如下
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
// 创建一个 2行3列的二维数组
int[][] array = {{1,2,3},{4,5,6}};
int[][] array2 = new int[][]{{1,2,3},{4,5,6}};
int[][] array3 = new int[2][3];
}
}
二维数组的打印
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
// 创建一个 2行3列的二维数组
int[][] array = {{1,2,3},{4,5,6}};
int[][] array2 = new int[][]{{1,2,3},{4,5,6}};
int[][] array3 = new int[2][3];
print(array);
}
public static void print(int[][] array){
// 按照我们以前对C的理解,二维数组的存储模式应该为 图60
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
//打印一行数据
System.out.print(array[i][j] + " ");
}
System.out.println();// 换行
}
}
}// 效果图 61
图60
图 61
由图的结果来看没问题
但是我总不能,每个二维数组都自己去算它有几个元素吧。
问题就在于怎么去获得 行 和 列
这里就引用C语言的一个概念,二维数组是一个特殊的一维数组
经过前面讲解,大家都知道数组是存储在堆上的,再加上面这句话的概念
让我们看看图62
图 62
接着我们来对上面的程序(二维数组的打印)进行改良
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
// 创建一个 2行3列的二维数组
int[][] array = {{1,2,3},{4,5,6}};
int[][] array2 = new int[][]{{1,2,3},{4,5,6}};
int[][] array3 = new int[2][3];
print(array);
// 这里我们在用实例证明一下
// array.length 是否能的得到 行数 2
System.out.println(array.length);
// array[下标].length 是否能得到 列数3
System.out.println(array[0].length);
System.out.println(array[1].length);
}
public static void print(int[][] array){
// 按照我们以前对C的理解,二维数组的存储模式应该为图60
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
//打印一行数据
System.out.print(array[i][j] + " ");
}
System.out.println();// 换行
}
}
}// 图63
图63
前面使用for循环来打印数组,这回我们foreach
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[][] array = {{1,2,3},{4,5,6}};
print(array);
}
public static void print(int[][] array){
for (int[] ret:array) {// array元素的数据类型是一个一维数组,我就需要一个相同的类型的变量来接收
for (int x:ret) {
// ret是一个一维数组的数组名,接下来就跟前面使用foreach是一样,将 引用ret 所指向的对象(数组)的元素,
// 读取并赋值给 与其元素类型相同的变量, 我们再将其输出。就可以了
System.out.print(x + " " );
}
System.out.println();//换行
}
}
}// 图 64
图64
还有一种输出二维数组的方法
在前面,我们使用了 Arrays.toString,将数组转换的字符串输出
二维数组也有对应的 方法: Arrays.deepToString(数组名)
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[][] array = {{1,2,3},{4,5,6}};
System.out.println(Arrays.deepToString(array));
}
}//图 65
图65
接下来,我会让你们见识到一种特别的二维数组
代码案例
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[][] array = {{1,2},{1,2,3}};
for (int[] ret:array) {
for (int x:ret) {
System.out.print(x+" ");
}
System.out.println();// 换行
}
}
}//图 66,见证奇迹,参考图62
图66
再来举个例子
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[][] array = new int[2][];
// 在C语言中,二维数组,是不能省略列的,行可以省略
// 而Java与之相反,行不能省略,列可以(图67、68)
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.print(array[i][j] + " ");
}
System.out.println();// 换行
}
}//图 69,图 70
}
图67
图68
图69
图70
哪有人可能会问,虽然写法没问题,但是程序运行会报错,那么省略列的意义在哪?
意义在于我们可以改,去赋予
这种二维数组,我们将其称为 不规则的二维数组,
代码如下
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[][] array = new int[2][];
array[0]=new int[3];// 意味着第一个数组长度为3
array[1]=new int[2];// 意味着第二个数组长度为2
System.out.println(Arrays.deepToString(array));
}
}// 图71,72