Java 基础语法深度剖析 Java 中的数组含数组练习
Posted 吞吞吐吐大魔王
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 基础语法深度剖析 Java 中的数组含数组练习相关的知识,希望对你有一定的参考价值。
文章目录
一、二维数组
和 C 语言一样,Java 中,二维数组本质上也就是一维数组,只不过每个元素又是一个一维数组
1. 创建数组
通过类比一维数组创建的方式,二维数组也有三种方式
-
静态初始化
int[][] array = {{1, 2, 3}, {4, 5, 6}};
-
动态初始化
int[][] array = new int[][]{{1, 2, 3}, {4, 5, 6}};
-
动态初始化
int[][] array = new int[2][3]; // 打印结果全为0
注意
在 Java 当中的二维数组不能省略行,但可以省略列。如
int[][] array = new int[2][];
但是这样如果不初始化,打印时会出现异常,因为没有列的话,相当于该二维数组的两个元素都为 null。
因此打印前要进行初始化,如
int[][] array = new int[2][]; array[0] = new int[4]; array[1] = new int[2]; System.out.println(Arrays.deepToString(array)); //打印结果为:[[0, 0, 0, 0], [0, 0]]
而这样的二维数组叫不规则二维数组,因为它的列是不确定的
2. 存储形式
和 C 语言不同,Java 中数组的存储是这样的
并且上图中 array 这个数组本身长度是2,并且每个元素又是一个一维数组
我们可以通过 array.length 验证长度为2的结论
int[][] array = {{1, 2, 3}, {4, 5, 6}};
System.out.println(array.length);
// 结果为:2
并且再用这个方式验证每个元素又是一个一维数组的结论
int[][] array = {{1, 2, 3}, {4, 5, 6}};
System.out.println(array[0].length);
System.out.println(array[1].length);
// 打印结果为
// 3
// 3
3. 使用方式
这里就讲介绍下遍历打印二位数组,至于二维数组的一些其他使用,其实都是建立在一维数组的基础上的,只要一维玩的溜,多维也飞起!
-
使用 for 循环遍历打印时
int[][] array = {{1, 2, 3}, {4, 5, 6}}; for(int i = 0; i < array.length; i++){ for(int j = 0; j < array[0].length; j++){ System.out.println(arrat[i][j] + " "); } }
-
使用 for-each 遍历打印时
int[][] array = {{1, 2, 3}, {4, 5, 6}}; for(int[] arr : array){ for(int x : arr){ System.out.println(x); } }
-
使用
Arrays.deepToString
打印int[][] array = {{1, 2, 3}, {4, 5, 6}}; System.out.println(Arrays.deepToString(array));
飞起
二、剖析 String[] args
你有没有想过每次我们用到的
public static void main(String[] args)
其中的 String[] args
是啥吗?我们可以知道它是一个字符串数组,那其中存放着什么呢?我们可以打印一下看看
public static void main(String[] args){
System.out.println(Arrays.toString(args));
}
// 结果为:[]
结果啥都没有,哦豁!上述是我在 IDEA 中尝试的,现在我再在命令行中试试
- 新建一个 Java 文件的代码,将上述代码输入,并保存(注意别忘了导包)
- 在该文件目录下通过
Shift+鼠标右键
,就可以找到 PowerShell,将其打开 - 通过 Javac 编译,java 运行该程序,结果为
人傻了,还是啥都没有
通过学习,如果我在运行时 java TestDemo
后面加上内容,如 Hello World!
就会有以下的结果
因为 main 函数里的 args 叫运行时命令行参数,如果运行java程序时,后面放了内容,就会将其存到该数组里面
三、数组练习
在训练今天的习题前,我们要理解什么是包。而在之前我们就已经用到过 java.util.Arrays
包
import java.util.Arrays;
public class TestDemo{
public static void main(String[] args){
int[] array = {1, 2, 3};
String ret = Arrays.toString(array);
System.out.println(ret);
}
}
// 打印结果为:[1, 2, 3]
这其中使用到的 toString 就是上述包中的一个方法,它可以让数组变成字符串并返回一个字符串。
因此学会该包中和数组相关的常用方法,那数组的使用就可以更加舒服了!那么什么是包呢?
例如你吃的一碗热干面,如果一碗面从头到尾要你自己做的话,你需要先和面、擀面、扯面、再烧水、煮面、放调料等等。而大多数店铺其实它们做面只要煮面放调料就行了,因为制作面的这个繁琐的过程可以直接通过去超市买面来省去。
一个程序的开发也不一定要我们从头到尾去完成,我们实际上可以站在巨人的肩膀上。就比如 Java 中有大量的标准库和海量的第三方库提供我们使用,这里面的代码就放在一个个的包中
除此之外我们要知道
上述包中的 Arrays 意思是操作数组的工具类
java.util 是 Arrays 所在的那个包的名字。大家可以去自己的
JDK\\jre\\lib\\rt.jar
中找到 util 这个包,里面包含了很多该包中已经封装好的字节码文件,要使用就导入对应的类就行我们也可以直接使用
import java.util.*;
,它可以导入该包中全部的类。(**注意:**其实在使用它时并不会将该包中的全部类导入,而是你使用到了哪个类,它才会加载哪个类)
那么接下来我们将开始数组的练习,想要玩数组必须要掌握的基础的方法!
1. 数组转字符串
我们知道 toString 方法可以直接将数组变成字符串,那么我们怎么自己实现一个方法模拟 toString 呢?
public static String myToString(int[] array){
if(array == null){
return null;
}
if(array.length == 0){
return "";
}
String str = "[";
for(int i = 0; i < array.length; i++){
if(i == 0){
str +=array[i];
}else{
str +=", " + array[i];
}
}
str += "]";
return str;
}
其中我们要注意数组长度为0或者数组为 null 的情况
2. 数组拷贝(4种)
首先我们自己可以尝试写一个方法来拷贝数组
public static int[] copyArray(int[] array){
int[] newArray = new int[array.length];
for(int i = 0; i < array.length; i++){
newArray[i] = array[i];
}
return newArray;
}
上述其实就是使用了 for 循环语句来进行遍历的拷贝,那么 Arrays 工具类中有没有更舒服的方式呢?
int[] array = {1, 2, 3};
int[] ret1 = Arrays.copyOf(array ,array.length);
System.out.println(Arrays.toString(ret1));
// 打印结果为:[1, 2, 3];
可以直接通过 copyOf(原数组,要返回的副本的长度) 方法来拷贝数组
如果副本长度大于原数组,相当于扩容,扩容的值默认为0
int[] array = {1, 2, 3};
int[] ret2 = Arrays.copyOf(array , 2 * array.length);
int[] ret3 = Arrays.copyOf(array , 2);
System.out.println(Arrays.toString(ret2));
System.out.println(Arrays.toString(ret3));
// 打印结果为:
// [1, 2 , 3, 0, 0, 0]
// [1, 2]
我们还可以通过 copyOfRange(原数组,起始索引值,终点索引值的后一个值) 方法来拷贝数组区间
int[] array = {1, 2, 3};
int[] ret4 = Arrays.copyOfRange(array ,1, 3);
System.out.println(Arrays.toString(ret4));
// 打印结果为:[2, 3]
除了上面所说的拷贝方式,我们还有其他方法。我们在 IDEA 上按住 Ctrl 键后点击 copyOf,跳转到它的源码,我们可以看到
通过比较我们自己创建的拷贝方法可以知道,中间这段代码(如下)就是拷贝的过程
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
我们再按住 Ctrl 键后点击 arraycopy,跳转到它的源码,我们可以看到
其中有一个 native,所以我们知道它是一个本地方法
而本地方法(native)具有以下特点
- 本地方法由 C/C++ 代码实现
- 运行速度快
- 运行时是在本地方法栈上
通过它的参数我们可以猜测,它是从原数组(src)的起始位置(srcPos)开始,拷贝到目的数组(dest)的起始位置(destPos)开始且长度为(length)
我们可以敲个代码验证
int[] array = {1, 2, 3};
int[] ret5 = new int[array.length];
System.arraycopy(array, 0, ret5, 0, array.length);
System.out.println(Arrays.toString(ret5));
// 打印结果为:[1, 2, 3];
不过要注意这里的 length 不能超过原数组的长度,不然就会出现数组越界的异常
那么拷贝的方式就这样就结束了吗?NO!
我们还可以调用对象的 clone() 方法进行拷贝
int[] array = {1, 2, 3};
int[] ret6 = array.clone();
System.out.println(Arrays.toString(ret6));
// 打印结果为:[1, 2, 3];
至于克隆的过程其实可以用一张图描绘
以上总结了数组的四种拷贝方法。但是我们要注意一种代码
int[] array = {1, 2, 3};
int[] ret7 = array;
这个根本就不是拷贝,因为 ret7 这个引用是指向 array 这个引用所指向的对象,如果 ret7 被改变, array 也会被改变
小结
上述介绍了拷贝的几种方法,但是拷贝其实还可以被分为深拷贝和浅拷贝。这里我简单的介绍一下
- 深拷贝:(数组里面存的一般是基本数据类型)
如果将 array2[0] 改成 99,array1[0] 不会被影响,这种拷贝就是深拷贝
- 浅拷贝:(数组里面存的是引用数据类型)
如图,加入原数组 array1[0] 存的是一个引用类型,它指向120这个对象。当数组拷贝后,array2[0] 这个引用也会指向120这个对象。如果将 array2[0] 所指向的对象改变,那么 array1[0] 所指向的对象也就改变了,这种就叫浅拷贝
那如何对上述这种例子的浅拷贝怎么改成深拷贝呢?我们可以先将对象拷贝一份,然后再将拷贝后对象的地址拷贝到新数组中
那么如何判断上述四种方法是深拷贝还是浅拷贝呢?一图让你理解明白
3. 找数组中的最大元素
这题就很简单啊,一首 for 循环送给你
public static void main(String[] args){
int[] array = {1,6,2,0,8};
System.out.println(maxNum(array));
}
public static int maxNum(int[] array){
int max = array[0];
for(int i = 1; i < array.length; i++){
if(array[i] > max){
max = array[i];
}
}
return max;
}
4. 求数组中元素的平均值
这题也很简单,直接上代码
public static double avg(int[] array){
int sum = 0;
for(int x: array){
sum += x;
}
return (double)sum/(double)array.length;
}
不够要注意返回的可能时浮点数
5. 查找数组中指定元素(顺序查找)
直接上代码
public static int findNum(int[] array, int x){
for(int i = 0; i < array.length; i++){
if(array[i] == x){
return i;
}
}
return -1;
}
最后返回-1,是因为数组索引不可能为-1,其他非索引值也行,只是一个判断不存在的值
6. 查找数组中指定元素(二分查找)
这也直接上代码啦!不过使用二分的前提是该数组为有序的
public static int binarySearch(int[] array, int x){
int left = 0;
int right = array.length - 1;
while(left <= right){
int mid = (left + right) >> 1;
if(array[mid] > x){
right = mid -1;
}else if(array[mid] < x){
left = mid + 1;
}else{
return mid;
}
}
return -1;
}
除了自己定义的方法,Arrays 类中也有一个 binarySearch 方法可以进行二分查找
public static void main(String[] args){
int[] array = {1, 3, 7 ,9 ,10};
System.out.println(Arrays.binarySearch(array, 9));
}
7. 检查数组的有序性
直接上自己写的方法
public static boolean isSorted(int[] array){
for(int i = 0; i < array.length - 1; i++){
if(array[i] > array[i+1]){
return false;
}
}
return true;
}
注意,遍历时 i 不能等于array.length,因为如果数组本身有界,则最后 array[i+1] 就会越界
8. 数组排序(冒泡排序)
这个题学 C 语言时也写烂了,直接上代码
public static void dubbleSort(int[] array){
for(int i = 0; i < array.length - 1; i++){
boolean flg = false;
for(int j = 0; j < array.length - i - 1; j++){
if(array[j] > array[j + 1]){
int tmp = array[j];
array[j] = array[ j 以上是关于Java 基础语法深度剖析 Java 中的数组含数组练习的主要内容,如果未能解决你的问题,请参考以下文章