前言:
本篇博客将为大家介绍Java中的数组的相关知识。
目录
基本介绍
概念相关
数组可以看成是相同类型元素的集合。在内存中是一段连续的空间,每个空间都有自己的编号,编号从0开始。
数组的使用




数组是引用类型
首先,我们先基本了解一下JVM:

程序计数器(PC Register):一块很小的空间,保存下一条执行指令的地址。
虚拟机栈(JVM Stack):与方法调用的一些相关信息,每个方法在执行的时候,都会先创建一个栈帧,栈帧中包含有:局部变量表、操作数栈,动态链接,返回地址以及一些其他的信息,保存的都是一些和方法执行时的相关信息。如:局部变量。方法在运行完以后,栈帧就会被销毁,即保存在栈帧中的数据也会被销毁。
本地方法栈(Native Method Stack):本地方法栈和虚拟机栈相似,只不过保存的是Native方法的局部变量。有些版本的JVM(如HotSpot)实现中本地方法栈和虚拟机栈是一起的。
堆(Head):JVM所管理的最大内存区域,使用new创建的对象都是在堆上保存的。堆是随着程序开始运行时而创建的,随着程序的退出而销毁,堆中的数据只要还有在使用的,就不会被销毁。
方法区(Method Area):用于存储已经被虚拟机加载的类信息,常量、静态变量、即时编译器编译后的代码等数据。方法编译出的字节码就是保存在这个区域的。
接下来,我们将主要涉及两个部分:堆和虚拟机。
基本数据类型创建的变量,称为基本变量,该变量空间存放的就是其保存的值;
而引用数据类型创建的变量,一般称为对象的引用,其空间存放的对象的地址。
举例:
注意:

图解:

应用场景
保存数据

作为方法的参数

图解:
作为方法的返回值

把func3方法中返回的arr的地址给了ret,使得ret指向了ret这个引用指向的地址(func3中的代码也可以直接简化成return new int[]{1,2,3};)
注意:对象一定是在堆上的,引用变量不一定是在栈上的。
练习
数组转字符串

记得要导包:import java.util.Arrays;(java.util.Arrays包里面包含了一些操作数组的常用方法)
我们也可以自己尝试实现一下这个方法:
- public class Demo3 {
- public static String myToString(int[] arr){
- String ret ="[";
- for (int i = 0; i < arr.length; i++) {
- ret += arr[i];
- if(i != arr.length -1){
- ret += ",";
- }
- }
- ret += "]";
- return ret;
- }
- public static void main(String[] args) {
- int[] arr = {1,2,3};
- System.out.println(myToString(arr));
- }
- }
数组拷贝
单纯的拷贝:
这里要注意,不能直接int[] copy = arr; 这样不是拷贝,是让copy拿到了arr里面放的地址,从而指向了它指向的引用,并没有创建新的数组,不是拷贝。
法二:
直接利用Arrays自带的拷贝方法(copyOf(原数组,新数组长度)):
值得注意的是:copyOf方法有很多方法和它构成重载,我们只需要在选择满足自己需求的那个即可。
copyOf方法还可以改变新的数组的长度:
拷贝一定范围的数据:
copyOfRang(原数组,初始拷贝下标,结束拷贝下标):


如果拷贝的范围超出了原来数组的长度,就只能能拷贝多少拷贝多少,那些没拷贝到的就还是初始值0。 
System.arraycopy(原数组,原数组开始被拷贝的下标,新数组,新数组开始拷贝的下标,拷贝的长度);

注意:方法中的最后一个数是从原数组中拷贝过来的数据的长度要格外小心是否越界,要注意在起始拷贝下标的基础上新数组是否能接收这么长的数据,原数组是否能给出这么多的数据供拷贝。
求数组中元素的平均值
- import java.util.Arrays;
- public class Demo3 {
- public static double avg(int[] arr){
- int sum = 0;
- for (int i = 0; i < arr.length; i++) {
- sum += arr[i];
- }
- return sum/(double)arr.length;//注意类型转换
- }
- public static void main(String[] args) {
- int[] arr = {1,2,3,4,5,6,7,8,21};
- System.out.println(avg(arr));
- }
- }

查找数组中的指定元素(二分查找+顺序查找)
顺序查找:
- import java.util.Arrays;
- public class Demo3 {
- //顺序查找:
- public static int findVal(int[] arr, int i){
- for (int j = 0; j < arr.length; j++) {
- if(arr[j] == i){
- return j;
- }
- }
- return -1;//数组下标不可能是负数,返回负数说明没有找到
-
- }
- public static void main(String[] args) {
- int[] arr = {1,2,3,4};
- int ret = findVal(arr,2);
- System.out.println(ret);
- }
- }

二分查找:针对有序数组,我们还可以通过二分查找来找出要查找的数据的下标:
- public class Demo3 {
- // 二分查找:
- public static int binarySearch(int[] arr, int i){
- int left = 0;
- int right = arr.length - 1;
-
- while(left <= right){
- int mid = (right + left)/2;
- if(arr[mid] < i){
- left = mid + 1;
- mid = (right + left)/2;
- }else if(arr[mid] > i){
- right = mid - 1;
- mid = (right + left)/2;
- }else if(arr[mid] == i){
- return mid;
- }
- }
- return -1;
- }
- public static void main(String[] args) {
- int[] arr = {12,34,45,51,54,65,76,87,97,99};
- int ret = binarySearch(arr,51);
- System.out.println(ret);
- }
- }

图解:

当然,我们也可以通过二分查找查找无序的数组的特定元素:先通过Arrays里面的sort方法对数组进行排序(升序),然后再通过二分查找查出排序好以后的特定数组的下标,不过这样做的意义其实并不大,因为经过排序,数组的下标早已不是原来的样子了。
数组排序(冒泡排序)
- import java.util.Arrays;
- public class Demo3 {
- //冒泡排序:
- public static void bubbleSort(int[] arr){
- for (int i = 0; i < arr.length-1; i++) {
- //一共要排arr.length-1次序,每排一次都会让数组中的一个数有序(此处以最大值为例)
- for (int j = 0; j < arr.length-i-1; j++) {
- //每一次排序内部要走arr.length-1-i次排序,才能实现让数组中为排好序的数里面的最大值排到最后
- if(arr[j]>arr[j+1]){
- int tmp = arr[j];
- arr[j] = arr[j+1];
- arr[j+1] = tmp;
- }
- }
- }
- }
- public static void main(String[] args) {
- int[] arr = {12,45,97,76,51,34,87,54,65,99};
- bubbleSort(arr);
- System.out.println(Arrays.toString(arr));
- }
- }
由图可知,在i=4开始,数组就已经是有序的了,后面的比较就是冗余的了,所以我们可以再对这个代码进行优化:在方法中加一个变量用来判断是否发生了交换,且每有一趟循环走完开始下一趟循环的时候就要让这个变量变回到它原来的值。一旦发现不再发生交换的时候,就直接跳出循环。
- import java.util.Arrays;
- public class Demo3 {
- //冒泡排序:
- public static void bubbleSort(int[] arr){
- for (int i = 0; i < arr.length-1; i++) {
- //一共要排arr.length-1次序,每排一次都会让数组中的一个数有序(此处以最大值为例)
- boolean flg = false;//每一趟开始的时候都要让flg为false
- for (int j = 0; j < arr.length-i-1; j++) {
- //每一次排序内部要走arr.length-1-i次排序,才能实现让数组中为排好序的数里面的最大值排到最后
- if(arr[j]>arr[j+1]){
- int tmp = arr[j];
- arr[j] = arr[j+1];
- arr[j+1] = tmp;
- flg = true;//若发生交换,则改变flg的值,通过flg的值来确定是否发生交换,若未发生交换,则直接跳出循环
- }
- }
- if(flg == false){
- break;
- }
- }
- }
- public static void main(String[] args) {
- int[] arr = {12,45,97,76,51,34,87,54,65,99};
- bubbleSort(arr);
- System.out.println(Arrays.toString(arr));
- }
- }
数组逆序
此处介绍的是直接通过交换对应下标的元素来实现数组逆序的效果。
- import java.util.Arrays;
- public class Demo3 {
- //数组逆序:
- public static void reserve(int[] arr){
- int left = 0;
- int right = arr.length-1;
- while(left < right){
- //不用管相等的情况,因为当right和left相等时就表明相等的那个数就是中间的那个,
- // 既然在最中间,也没有交换的必要了
- int tmp = arr[left];
- arr[left] = arr[right];
- arr[right] = tmp;
- left++;
- right--;
- }
- }
- public static void main(String[] args) {
- int[] arr = {12,45,97,76,51,34,87,54,65,99};
- reserve(arr);
- System.out.println(Arrays.toString(arr));
- }
- }

二维数组




Arrays中的其他方法的介绍:


同时,fill方法有很多重载方法,可以根据需求选择,如:把某个值从数组的n1下标填充到n2下标