[Java基本语法] 数组及其应用

作者 : admin 本文共8738个字,预计阅读时间需要22分钟 发布时间: 2024-06-15 共1人阅读

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀线程与网络(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
[Java基本语法] 数组及其应用插图

目录

  • 1. 数组
    • 1.1 数组的认识
      • 1.1.1 数组的基本概念
        • 1.1.1.1 什么是数组
        • 1.1.1.2数组的创建与初始化
        • 1.1.1.3数组的初始化
        • 1.1.1.4数组的使用
      • 1.1.2 组是引用类型
        • 1.1.2.1基本类型和引用类型在底层上的区别
        • 1.1.2.2认识null
      • 1.1.3 数组的使用场景
    • 1.2 数组的应用与常用方法
      • 1.2.1 数组转字符串
      • 1.2.2 数组拷贝
      • 1.2.3 求数组中的平均值
      • 1.2.4 顺序查找数组元素
      • 1.2.5 二分查找数组元素
      • 1.2.6 数组的排序
      • 1.2.7 数组逆序
      • 1.2.8 二维数组

1. 数组

1.1 数组的认识

声明:为什么要在这里讲数组,我的思路是这样的,因为Arrays(数组)本身就是一个类,创建数组时,我们可以用类与对象的思想来理解,而且这个类中有很多方法,对数组进行操作时,我们需要调用这些方法,我们为了大家看代码更加方便,所以我们选择在讲完类与对象和继承与多态之后再讲数组,避免了大家在数组的学习中出现“看不懂代码”的尴尬,再来大家在C语言中一定学过数组,理解起来会比较轻松,我们会与C语言中的数组进行比较,充分体现我们Java语言的面相对象性。

1.1.1 数组的基本概念

1.1.1.1 什么是数组

数组,可以看做是相同类型元素的集合。在内存中是一段连续的空间,就好像车库一样,有一块一块的空间,在这个空间中都停着同一类事物——汽车。

[Java基本语法] 数组及其应用插图(1)
数组有以下几个特征:

  • 数组存放的元素其类型相同
  • 数组的空间是连续的
  • 每个空间都有自己的编号,起始位置的编号为0
1.1.1.2数组的创建与初始化
  1. 数组的创建
    数据类型[] 数组名=new 数据类型[数组大小]
    这里我们看到了关键字new,说明我们创建了一个数组对象,与C语言不同的是中括号的位置,其实我们可以把数据类型加上中括号当做一个整体来理解,中括号可以看做是数组的标志。所以我们把它理解为这个对象的类型为什么样数据类型的一个数组,这时你就会发现,怪的其实是C语言。
//举个例子
int[] array1=new int [10];
1.1.1.3数组的初始化

数组的初始化分为动态初始化和静态初始化

  1. 动态初始化:在创建数组的时候,直接指定数组中的元素个数,就是我们上面创建数组的方法
  2. 静态初始化:在创建数组的时候不指定数组的个数,而是直接将具体的内容进行指定
    语法格式:数据类型[] 数组名称={data1,data2,data3…}
    或者我们也可以使用类与对象的思想来创建:数据类型[] 数组名称=new 数据类型[] {data1,data2,…}
int[] array1 = new int[]{0,1,2,3,4,5,6,7,8,9};
double[] array2 = new double[]{1.0, 2.0, 3.0, 4.0, 5.0};
String[] array3 = new String[]{"hell", "Java", "!!!"};

【注意事项】

  • 静态初始化虽然没有指定数组的长度,编译器在编译时会根据{}中的元素个数来指定数组长度
  • 静态初始化时,{}中的数据必须与[]前的数据类型一致
  • 静态初始化时,虽然可以省略new,但是编译器在编译时会把new还原出来
  • 在创建和初始化分开时,静态初始化不可以省略new
int[] array1;
array1 = new int[10];
int[] array2;
array2 = new int[]{10, 20, 30};
// 注意省略格式不可以拆分, 否则编译失败
// int[] array3;
// array3 = {1, 2, 3};
  • 如果没有对数组进行初始化,数组中有默认值
类型默认值
byte0
short0
int0
long0
float0.0f
double0.0
char/u0000
booleanfalse
1.1.1.4数组的使用
  1. 数组的访问
    数组可直接通过其下标来访问
int[]array = new int[]{10, 20, 30, 40, 50};
System.out.println(array[0]);
System.out.println(array[1]);
System.out.println(array[2]);
System.out.println(array[3]);
System.out.println(array[4]);
// 也可以通过[]对数组中的元素进行修改
array[0] = 100;
System.out.println(array[0]);

注意:数组不可以越界访问,必须介于[0,N)之间
2. 数组的遍历
所谓遍历,就是将数组的元素全部访问一遍,若我们有以下代码:

int[]array = new int[]{10, 20, 30, 40, 50};
System.out.println(array[0]);
System.out.println(array[1]);
System.out.println(array[2]);
System.out.println(array[3]);
System.out.println(array[4]);

上述代码的问题是:一条一条地输出语句显得非常麻烦,所以我们考虑使用循环语句

int[]array = new int[]{10, 20, 30, 40, 50};
for(int i = 0; i < 5; i++){
	System.out.println(array[i]);
}

上述代码的问题是,我们如果花括号中有好多好多元素,我们要一个一个地数吗,当然不是,Arrays类中为我们提供了非常便利的一个方法:length方法,我们可以通过数组名.length来获取到数组的长度

int[]array = new int[]{10, 20, 30, 40, 50};
for(int i = 0; i < array.length; i++){
	System.out.println(array[i]);
}

也可以通过for-each遍历数组

int[] array = {1, 2, 3};
for (int x : array) {
	System.out.println(x);
}

也可以将数组转化为字符串进行输出

System.out.println(Arrays.toString(array));

这里通过Arrays的类名来调用了toString方法,在底层代码中,其实是Arrays类对他的父类Object类的toString方法进行了重写,所以才有了这么方便的一个方法。

1.1.2 组是引用类型

1.1.2.1基本类型和引用类型在底层上的区别

基本类型在栈中创建的变量,数值直接存放在栈上,例如:int,char,byte等,但是引用类型不是,在栈上存放的是一个引用类型对象的地址,可以理解为首元素地址,我们一般称它为对象的引用,真正的内容存放在堆中
[Java基本语法] 数组及其应用插图(2)

1.1.2.2认识null

null 在 Java 中表示 “空引用” , 也就是一个不指向对象的引用.

int[] arr = null;
System.out.println(arr[0]);

执行结果
Exception in thread “main” java.lang.NullPointerException
at Test.main(Test.java:6)

null 的作用类似于 C 语言中的 NULL (空指针), 都是表示一个无效的内存位置. 因此不能对这个内存进行任何读写操
作. 一旦尝试读写, 就会抛出 NullPointerException.这个东西叫做空指针异常,我们在后面介绍异常的时候详细解释

1.1.3 数组的使用场景

  1. 保存数据
public static void main(String[] args) {
	int[] array = {1, 2, 3};
	for(int i = 0; i < array.length; ++i){
		System.out.println(array[i] + " ");
	}
}
  1. 作为方法的参数
    这点与C语言不同,既然数组数组是引用数据类型,既然是数据类型,就可以作为方法的参数
public static void main(String[] args) {
	int[] arr = {1, 2, 3};
	func(arr);
	System.out.println("arr[0] = " + arr[0]);
}
public static void func(int[] a) {
	a[0] = 10;
	System.out.println("a[0] = " + a[0]);
}

执行结果
a[0] = 10
arr[0] = 10

发现在func方法内部修改数组的内容, 方法外部的数组内容也发生改变.
因为数组是引用类型,按照引用类型来进行传递,是可以修改其中存放的内容的。底层图解如下:
[Java基本语法] 数组及其应用插图(3)
【总结】所谓的 “引用” 本质上只是存了一个地址. Java 将数组设定成引用类型, 这样的话后续进行数组参数传参, 其实只是将数组的地址传入到函数形参中.

  1. 作为方法的返回类型
    比如获取斐波那契数列的前N项:
public class TestArray {
	public static int[] fib(int n){
	if(n <= 0){
		return null;
	}
	int[] array = new int[n];
	array[0] = array[1] = 1;
	for(int i = 2; i < n; ++i){
		array[i] = array[i-1] + array[i-2];
	}
	return array;
}
public static void main(String[] args) {
	int[] array = fib(10);
	for (int i = 0; i < array.length; i++) {
		System.out.println(array[i]);
		}
	}
}

1.2 数组的应用与常用方法

1.2.1 数组转字符串

int [] array = {0,1,2,3,4}
System.out.println(Arrays.toString(array));
//调用Arrays类中重写的toString方法
//执行结果:[0,1,2,3,4]

这里使用了Arrays类中重写的toString方法,源码如下:
为了大家方便阅读代码,我们在其中添加注释

public static String toString(int[] a) {
        if (a == null)
            return "null";//数组为null返回null
        int iMax = a.length - 1;
        if (iMax == -1)//数组为空返回空数组(a.length==0)
            return "[]";

        StringBuilder b = new StringBuilder();//创建StringBuilder
        b.append('[');//首先为b添加“[”
        for (int i = 0; ; i++) {
            b.append(a[i]);//遍历数组,为b添加元素
            if (i == iMax)
                return b.append(']').toString();//遍历到数组最后停止,并添加“]”
            b.append(", ");
        }
    }

1.2.2 数组拷贝

public static void func(){
	// newArr和arr引用的是同一个数组
	// 因此newArr修改空间中内容之后,arr也可以看到修改的结果
	int[] arr = {1,2,3,4,5,6};
	int[] newArr = arr;
	newArr[0] = 10;
	System.out.println("newArr: " + Arrays.toString(arr));
	// 使用Arrays中copyOf方法完成数组的拷贝:
	// copyOf方法在进行数组拷贝时,创建了一个新的数组
	// arr和newArr引用的不是同一个数组
	arr[0] = 1;
	newArr = Arrays.copyOf(arr, arr.length);
	System.out.println("newArr: " + Arrays.toString(newArr));
	// 因为arr修改其引用数组中内容时,对newArr没有任何影响
	arr[0] = 10;
	System.out.println("arr: " + Arrays.toString(arr));
	System.out.println("newArr: " + Arrays.toString(newArr));
	// 拷贝某个范围.
	int[] newArr2 = Arrays.copyOfRange(arr, 2, 4);
	System.out.println("newArr2: " + Arrays.toString(newArr2));
}

我们前面说到,数组类型的对象是引用对象,所以我们通过画堆和栈的方法来解释以上的代码:

  • 第一段代码:
    [Java基本语法] 数组及其应用插图(4)
  • 第二段代码
    [Java基本语法] 数组及其应用插图(5)
  • 第三段代码
    [Java基本语法] 数组及其应用插图(6)
    以下是关于copyOf和copyOfRage的源码:
    我们还是通过注释解释
public static int[] copyOf(int[] original, int newLength) {//原数组和新数组的长度
        int[] copy = new int[newLength];//创建新数组,所以对原来的数组没有影响
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));//该行代码实现源码比较复杂,这里不多解释
        return copy;//返回复制后的数组
    }

在后期我们学习数据结构的时候,会反复使用该方法对顺序表,优先级队列,栈等的数据结构进行扩容。
我们下面使用一个简单的方法实现一个自己的copyOf方法

 public int[] myCopyOf(int[] array,int newLength){
        int [] newArray = new int[newLength];
        for (int i = 0; i < array.length; i++) {
            newArray[i] = array[i];
        }
        return newArray;
    }

1.2.3 求数组中的平均值

public static void main(String[] args) {
	int[] arr = {1,2,3,4,5,6};
	System.out.println(avg(arr));
}
public static double avg(int[] arr) {
	int sum = 0;
	for (int x : arr) {
	sum += x;
	}
	return (double)sum / (double)arr.length;//注意强制类型转换
}
// 执行结果
//3.5

1.2.4 顺序查找数组元素

public static void main(String[] args) {
	int[] arr = {1,2,3,10,5,6};
	System.out.println(find(arr, 10));
}
public static int find(int[] arr, int data) {
	for (int i = 0; i < arr.length; i++) {
	if (arr[i] == data) {
		return i;
		}
	}
	return -1; // 表示没有找到
}
// 执行结果
//3分为

上述代码在查找上有缺陷,这种方法很慢,我们下面介绍一种比较高效的方法

1.2.5 二分查找数组元素

这种方法虽然相较顺序查找比较高效,但是也有一定的限制,这种方法只对有序数组生效,那么什么叫有序数组?有序数组分为升序和降序,如:1 2 3 4, 4 3 2 1,我们以升序数组为例
二分查找的大体思路为:

  • 把索要查找的元素和中间的元素进行比较
  • 若等于中间元素,则要查找的元素就是中间元素
  • 若小于中间元素,则去数组左侧查找
  • 若大于中间元素,则去数组右侧查找
public static void main(String[] args) {
	int[] arr = {1,2,3,4,5,6};
	System.out.println(binarySearch(arr, 6));
}
public static int binarySearch(int[] arr, int toFind) {
	int left = 0;
	int right = arr.length - 1;
	while (left <= right) {
	int mid = (left + right) / 2;
	if (toFind < arr[mid]) {
	// 去左侧区间找
	right = mid - 1;
	} else if (toFind > arr[mid]) {
	// 去右侧区间找
	left = mid + 1;
	} else {
	// 相等, 说明找到了
	return mid;
	}
	}
	// 循环结束, 说明没找到
	return -1;
	}
	// 执行结果
	//5

1.2.6 数组的排序

给定一个数组,我们来将这个数组排序,排列为升序或降序,假设我们要排升序

  1. 我们排序时可以使用冒泡排序法,当然,冒泡排序法只是其中一种方法,在后续学习数据结构时,我们会学习更多排序方法,例如:堆排序,快速排序等

冒泡排序大体思路:

  • 确定排序趟数
  • 确定每一趟排序的次数
  • 第一趟,从开头开始,将相邻元素两两比较,第二趟,从第二个元素开始,重复第一次的操作…

我们通过一个动图来形象地描述冒泡排序:
[Java基本语法] 数组及其应用插图(7)
我们通过代码来实现:

public static void main(String[] args) {
	int[] arr = {9, 5, 2, 7};
	bubbleSort(arr);
	System.out.println(Arrays.toString(arr));
}
public static void bubbleSort(int[] arr) {
	for (int i = 0; i < arr.length; i++) {
		for (int j = 1; j < arr.length-i; j++) {
			if (arr[j-1] > arr[j]) {
			int tmp = arr[j - 1];
			arr[j - 1] = arr[j];
			arr[j] = tmp;
			}
		}
	} // end for
} // end bubbleSort
// 执行结果
//[2, 5, 7, 9]
  1. 我们也可以使用选择排序来排序

大体思路:

  • 先把第一个数据作为最小数据,之后一一与后面的元素进行比较
  • 若遇到比第一个元素小的元素,则替换最小元素
  • 遍历到最后,将第一个与最小元素交换
  • 走到第二个元素,重复上述操作
    我们依然通过动图来解释:
    [Java基本语法] 数组及其应用插图(8)
    代码如下:
 public int[] selectSort(int[] array){
        for (int i = 0; i < array.length; i++) {
            int minIndex = i;
            int j = i+1;
            for (; j < array.length; j++) {
                if(array[j]<array[minIndex]){
                    minIndex = j;
                }
            }
            int tmp = array[minIndex];
            array[minIndex]=array[i];
            array[i] = tmp;
        }
        return array;
    }

冒泡排序性能较低. Java 中内置了更高效的排序算法,即sort方法

public static void main(String[] args) {
	int[] arr = {9, 5, 2, 7};
	Arrays.sort(arr);
	System.out.println(Arrays.toString(arr));
}

Java系统中实现sort方法的底层排序方法是快速排序,我们后续介绍

1.2.7 数组逆序

给定一个数组, 将里面的元素逆序排列.
思路:

  • 设定两个下标, 分别指向第一个元素和最后一个元素. 交换两个位置的元素.
  • 然后让前一个下标自增, 后一个下标自减, 循环继续即可.

代码示例:

public static void main(String[] args) {
	int[] arr = {1, 2, 3, 4};
	reverse(arr);
	System.out.println(Arrays.toString(arr));
}
public static void reverse(int[] arr) {
	int left = 0;
	int right = arr.length - 1;
	while (left < right) {
		int tmp = arr[left];
		arr[left] = arr[right];
		arr[right] = tmp;
		left++;
		right--;
	}
}

1.2.8 二维数组

二维数组本质上也就是一维数组, 只不过每个元素又是一个一维数组

  • 基本语法:
    数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };
  • 代码实例
int[][] arr = {
	{1, 2, 3, 4},
	{5, 6, 7, 8},
	{9, 10, 11, 12}
	};
for (int row = 0; row < arr.length; row++) {
	for (int col = 0; col < arr[row].length; col++) {
		System.out.printf("%d	", arr[row][col]);
	}
	System.out.println("");
}
// 执行结果
1 2 3 4
5 6 7 8
9 10 11 12

二维数组在后面其实使用的比较少,我们在后面使用二维数组时一般都是通过顺序表结合泛型来创建

本站无任何商业行为
个人在线分享 » [Java基本语法] 数组及其应用
E-->