走进Java王国-------从0到1的旅程 05数组

本文详细介绍了Java中的数组,包括其本质、创建与初始化、使用方法,以及数组作为数据结构的高效读写特性。此外,还探讨了数组在内存中的存储方式,特别是数组作为引用类型在方法间传递的原理。文中通过示例展示了数组的遍历、拷贝、转换为字符串、查找最大值等常见操作,并讲解了二分查找法的实现。最后,通过汉诺塔问题的递归解决展示了递归思想的应用。
摘要由CSDN通过智能技术生成

 

一、什么是数组:

​ 数组本质上就是让我们能"批量"创建相同类型的变量,一次定义N个相同数据类型的变量,我们就把这种结构称之为数组。比如:假设我现在要一次性创建1w个整型变量,我们就要用到数组这个结构

二、什么是数据结构:把一堆数字保存起来的结构就是数据结构,数据结构关心的是如何高效的读写数据

三、数组的创建于初始化:

​ 1、数组的动态初始化:数据类型[] 数组名称 = new 数据类型{初始化数据},其中大括号中的内容可选

​ 也可以数据类型[] 数组名称 = new 数据类型[num],num指的是当前数组的最大元素个数

比如:int[] arr = new int {1,3,5,7,9};第一种动态初始化方法是每个元素同时赋值,而int[] arr = new int[5];这个是指在创建数组时,若没有使用{}来初始化每个元素的值,每个元素都是该数据类型的默认值,即0,0,0,0,0

​ 2、数组的静态初始化:数据类型[] 数组名称 = {初始化数据};比如:int[] arr = {1,3,5,7,9};和动态初始化本质一样,因为语法糖,经过javac编译之后,就是动态初始化,语法糖:只存在编译期之前的语法,编译器为了方便程序员简化写法,右键open in terminal可查看,经编译后,转化为int[] arr = new int[] {1,3,5,7,9};

四、数组的使用:

​ 1、 获取一个数组的长度(最多保存的元素个数),使用数组名称.length 示例如下:

public class ArrTest {
    public static void main(String[] args) {
        int[] arr = new int[]{1,3,5,7,9};
        int[] arr1 = new int[5];
        System.out.println(arr.length);//长度都为5
        System.out.println(arr1.length);//长度都为5
    }
}


​ 2、如何访问数组元素

​ 使用数组名称[要访问的元素相较于第一个元素的偏移量]

​ 使用数组名称[元素的索引] 比如:int[] arr1 = new int[]{1,3,5,7,9};要获取第一个元素 就是arr1[0];数组的索引从0开始,最后一个元素的索引arr1.length-1

​ 为什么要从0开始呢?索引其实就是"偏移量",相较于数组的第一个元素的单位长度,数组在内存中存储时,每个元素之间都是顺序存储的,保存的其实是数组的首元素地址。要找到其他元素,只要知道其他元素相较于第一个元素的距离就能找到。(一层楼有10个宿舍,我要找第5个宿舍,首先你得找到第一个宿舍在哪)

​ 如果访问了一个数组索引并不存在的元素会咋样?比如:System.out.println(arr[5]);一定是访问了一个非法索引,这个索引在当前数组中根本就不存在,会显示数组越界异常的错误

​ 遍历数组的每个元素 示例如下:

int[] arr2 = new int[]{1,3,5,7,9};
//访问数组arr2的每个元素
//第一种方法,此时i表示数组中每个元素的索引下标
for (int i = 0; i < arr2.length; i++) {
    System.out.print(arr[i] +"、");
}
//第二种方法,JDK1.5引入的for-each循环,增强型for循环
//此处的i指的是从数组第一个元素开始取值,第一次把第一个元素的值赋值一份给i,第二次循环把第二个元素的值赋值一份给i
//依此类推,直到整个数组都遍历结束
for (int i:arr2){
    System.out.print(i + "、");


​ 其中,关于for-each循环:

//此时for-each循环的i只能读取数组的元素值,无法修改!!!
//i是原数组每个元素的值拷贝,并不是实实在在的数组元素
for (int i:arr2){
    if (i == 5){
        i = 55;
    }
}
System.out.println("第三个元素的值为:" + arr2[2]);


五、数组作为第一个引用数据类型 int[] arr = new int[5]具体来说就是整型数组的引用,数组和方法之间存在一些关系(重点!!!重点!!!)

​ 1、数组作为方法的参数

​ 创建一个方法,接收任意的整型数组并打印。示例如下:

    System.out.println();
    int[] arr3 = {1,3,5};
    int[] arr4 = {2,4,6,8};
    printNum(arr3);
    printNum(arr4);
}
public static void printNum(int[] num){
    for (int i:num){
        System.out.print(i+"、");
    }
}


​ 2、关于引用数据类型的理解问题:

引入:(1)参考笔记实参与形参的关系,实参是位于主方法的临时变量,形参是位于swap方法的临时变量,而JVM把内存划分为6个区域,今天重点记栈区和堆区,方法的调用就是在栈区进行的,每个方法的调用过程,就是一个栈帧的入栈以及出栈的过程。栈采取LIFO 先进后出,后进先出的结构,所以先将main方法入栈,然后将swap方法入栈,方法中的局部变量和形参都在栈中存储,swap方法交换了x和y的值,可是由于后进先出,当方法调用结束出栈时,临时变量会被销毁。因此不改变main方法中实参的值,图见word文档

(2) JVM的另一块内存区域被称为"堆区",所有对象都在堆区存储,数组对象,累的实例化对象,接口的对象

示例:int[] arr = new int[]{10,20};new后面代表数组对象,在堆上存储,等号前面代表数组引用,引用就是起了个"别名",保存的数值就是该对象的地址。对于数组对象来说,数组引用实际上就是保存了数组的首元素地址。(重点!)类比现实生活:我就是一个人类对象,实实在在存在的,明哥、小明-->引用(给对象起了个名字);再比如,电视机--->实实在在存在的对象,遥控器就是这个电视机的引用,一个电视机可以有多个遥控器,拿着遥控器就能操作电视机的本质,就是遥控器内部保存了这个电视机的地址

(3) 实参到形参的传递仍然满足值传递,只是把主方法中int[] arr的地址拷贝一份复制给swap方法中 int[] arr,图见word文档,此时修改堆中数组对象的值,main方法中arr5中可见,因为main和swap方法都同时指向堆中同一区域,示例如下:

public class ArrTest {
    public static void main(String[] args) {
        int[] arr5 = new int[]{10,20};
        swapArr(arr5);
        System.out.println("arr[0] = " + arr5[0] +",arr[1] = " + arr5[1] );
	}
	 public static void swapArr(int[] arr){
	 //拿着swapArr方法中的数组引用swap-arr修改了堆中数组对象的值,这个修改对于主方法中arr5是可见的
	 //本质:这两个引用指向了堆中同一块内存区域!!!
	 //arr = new int[]{10,20};若加上这句代码,看见new关键字,一定要在堆中开辟新的空间,此时会有两个电视机,两个遥控器各自指各自的电视机,所以主方法中arr5的值不改变,图见word文档
        int temp = arr[0];
        arr[0] = arr[1];
        arr[1] = temp;
    }
}


(4) 补充:若此时想在main方法中调用swapArr中的数组值,先返回swap中的数组名称,然后要操作堆中的对象,必须通过引用来进行,必须知道这个对象的名字才能操作,示例如下:

public class ArrTest {
    public static void main(String[] args) {
        int[] arr5 = new int[]{10, 20};
        int[] ret = swapArr(arr5);
        System.out.println("arr[0] = " + arr5[0] + ",arr[1] = " + arr5[1]);
        System.out.println("ret[0] = " + ret[0] + ",ret[1] = " + ret[1]);
    }
     public static int[] swapArr(int[] arr){
        arr = new int[]{10,20};
        int temp = arr[0];
        arr[0] = arr[1];
        arr[1] = temp;
        return arr;
    }
}

数组练习题1:实现数组拷贝并打印数据元素的值

public class ArrayHomeWork {
    public static void main(String[] args) {
        //数组练习题1:实现数组拷贝并打印数据元素的值
        int[] arr = new int[] {1,2,5,8};
        int[] newArr = copyOf(arr);
        printArray(newArr);
    }
    //因为是拷贝一个整型数组,所以返回值是int[]
    public static int[] copyOf(int[] arr){
        int[] newArr = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            newArr[i] = arr[i];
        }
        return newArr;
    }
    public static void printArray(int[] arr){
        for (int i :arr){
            System.out.print(i + " ");
        }
    }
}


数组练习题2:数组对象转化为字符串对象

​ 以后看到JDK中的某些类,在类后面加s,这种类都是工具类,提供了大量有用的方法,Arrays数组的工具类--包含数组转字符串的方法,数组排序的方法等操作,collections集合工具类,使用工具类会产生导包

import java.util.Arrays;

public class ArrayHomeWork {
    public static void main(String[] args) {
        //数组练习题2:数组对象转化为字符串对象
        int[] arr = new int[]{1, 2, 5, 8};
        //方法一:利用Arrays工具进行转化
        String str = Arrays.toString(arr);
        System.out.println(str);
        //方法二:利用定义方法来转化
        System.out.println(arrToStr(arr));
    }
    public static String arrToStr(int [] arr){
        //利用String的拼接来做
        String str = "[";
        for (int i = 0; i < arr.length; i++) {
            str+=arr[i];
            if (i != arr.length-1){
                str+=", ";
            }
        }
        str+="]";
        return str;
    }
}


数组练习题3:利用Arrays类实现数组拷贝,并分类讨论新数组长度与原数组长度的不同情况

public class ArrayHomeWork {
    public static void main(String[] args) {
    //数组练习题3:利用Arrays类实现数组拷贝,并分类讨论新数组长度与原数组长度的不同情况
        //第一种情况:部分拷贝,完全拷贝就是一模一样
        int[] newArr = Arrays.copyOf(arr,2);
        System.out.println(arrToStr(newArr));
        //第二种情况:新数组长度大于原数组长度,全拷贝,剩余的数据元素用该数据类型的默认值补齐
        int[] newArr1 = Arrays.copyOf(arr,5);
        System.out.println(arrToStr(newArr1));
        //第三种情况:使用索引部分拷贝,一般都是左闭右开
        int[] newArr2 = Arrays.copyOfRange(arr,0,2);
        System.out.println(arrToStr(newArr2));
    }
}


数组练习题4:给定一个整数数组,如何找出这个数组的最大值

public class ArrayHomeWork {
    public static void main(String[] args) {
    int[] arr = new int[]{1, 2, 5, 8};
    System.out.println(max(arr));
    }
    public static int max(int[] arr){
    	//默认取第一个数组元素假定为最大值或最小值,而不能随机取一个数字
        int max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if (arr[i]>max){
                max = arr[i];
            }
        }
        return max;
    }
}


数组练习题5:查找一个数组中是否包含指定元素,若存在,返回该元素的索引,若不存在,返回-1

public class ArrayHomeWork {
    public static void main(String[] args) {
    int[] arr = new int[]{1, 2, 5, 8};
    System.out.println(find(arr,5));
    }
    public static int find(int[] arr,int toFind){
        for (int i = 0; i < arr.length; i++) {
            if (toFind==arr[i]){
                return i;
            }
        }
        //若没有找到指定元素
        return -1;
    }
}


数组练习题6.重点题 二分查找法 前提是数组有序 找到元素返回索引,没有找到返回-1

public class ArrayHomeWork {
    public static void main(String[] args) {
    	//数组练习题6.重点题 二分查找法 前提是数组有序 找到元素返回索引,没有找到返回-1
        System.out.println(binarySearch(arr,8));
    }
    //二分查找法
    public static int binarySearch(int[] arr,int toFind){
        int left = 0;
        int right = arr.length-1;
        //在arr[left..right]区间中查找元素toFind的索引
        //left==right,说明区间中还剩下一个元素没有判断,就要判断这个最后的元素
        //循环终止的条件一定是left>right,区间中一个元素都没有才终止循环
        while (left<=right) {
            //中间数据元素的下标
            int mid = (left + right) / 2;
            if (toFind==arr[mid]){
                System.out.println("找到元素了!");
                return mid;
            }else if (toFind>arr[mid]){
                left = mid+1;
            }else {
                right = mid-1;
            }
        }
        //该区间没有指定元素
        return -1;
    }
}


数组练习题7:尝试用递归的方法实现二分法查找

public class ArrayHomeWork {
    public static void main(String[] args) {
    	//数组练习题7:尝试用递归的方法实现二分法查找
        System.out.println(binarySearchRecursion(arr,5,0,arr.length-1));
    }
     public static int binarySearchRecursion(int[] arr,int toFind,int left,int right){
        //1、终止条件,如果left>right,空数组,返回-1,跳出
        if (left>right){
            return -1;
        }
        //若left<=right时
        int mid = (left+right)/2;
        if (toFind==arr[mid]){
            System.out.println("找到该元素了!");
            return mid;
        }else if (toFind<arr[mid]){
            return binarySearchRecursion(arr,toFind,left,mid-1);
        }
        return binarySearchRecursion(arr,toFind,mid+1,right);
    }
}

补充:通查找思路和二分查找思路的对比,示例如下:

​ 当一个有序集合的长度为n时,顺序查找法,比较的次数n;

​ 二分查找:n/2/2/2/2一直到==1 n=log2N(以2为底,N的对数) 对数操作:当一个算法不断除以一个数知道除为1或0,这个算法就是对数级别算法

public class ArrayHomeWork {
    static int count = 0;
    public static void main(String[] args) {
    //补充:普通查找思路和二分查找思路的对比
        int[] data = new int[10000];
        for (int i = 0; i < data.length; i++) {
            data[i] = i+1;
        }
        //顺序查找9999,需要查找多少次9999次
        System.out.println(find(data,9999));
        System.out.println(binarySearch(data,9999));//结果为倒数第二个的索引9998
        System.out.println("二分查找一共比较了:"+count+"次");//16次
    }
    public static int find(int[] arr,int toFind){
        for (int i = 0; i < arr.length; i++) {
            if (toFind==arr[i]){
                return i;
            }
        }
        //若没有找到指定元素
        return -1;
    }
    //二分查找法
    public static int binarySearch(int[] arr,int toFind){
        int left = 0;
        int right = arr.length-1;
        //在arr[left..right]区间中查找元素toFind的索引
        //left==right,说明区间中还剩下一个元素没有判断,就要判断这个最后的元素
        //循环终止的条件一定是left>right,区间中一个元素都没有才终止循环
        while (left<=right) {
            //可用于记录查找的次数
            count++;
            //中间数据元素的下标
            int mid = (left + right) / 2;
            if (toFind==arr[mid]){
                System.out.println("找到元素了!");
                return mid;
            }else if (toFind>arr[mid]){
                left = mid+1;
            }else {
                right = mid-1;
            }
        }
        //该区间没有指定元素
        return -1;
    }
}

补充:了解知识,二维数组,相比一维数组只是多了列这种概念

​ 语法:数据类型 数组名称 = new 数据类型行数{可选初始化数据},内部一个{}代表一行,行与行之间用逗号隔开,那么如何遍历二维数组呢?

public class ArrayHomeWork {
	public static void main(String[] args) {
		//补充:如何遍历二维数组
        int[][] arr1 = new int[][]{
                {1,2,3,4},
                {5,6,7,8},
                {9,10,11,12}
        };
        for (int row = 0; row < arr1.length; row++) {
            for (int col = 0; col < arr1[row].length; col++) {
                System.out.print(arr1[row][col] + "\t");
            }
        }
    }
}


递归重点习题:汉诺塔问题

public class ArrayHomeWork {
	public static void main(String[] args) {
		//递归重点习题:汉诺塔问题
        int n =3;
        Tower(n,'A','B','C');
    }
     public static void Tower(int nDisks,char A,char B,char C){
        //1、终止条件,或边界条件,当盘子数为1时,直接从A到C
        if (nDisks==1){
            move(nDisks,A,C);
            return;
        }
        //nDisks>=2时
        //核心步骤1,将n-1个盘子由A借助C移动到B
        Tower(nDisks-1,A,C,B);
        //核心步骤2,将A中最大的盘子由A移动到C
        move(nDisks,A,C);
        //核心步骤3,将B中n-1个盘子借助A由B移动到C
        Tower(nDisks-1,B,A,C);
    }
    //将原盘子从原塔移动到目标塔
    public static void move(int nDisks,char sourceTower,char destTower){
        System.out.println("编号为"+nDisks+"的盘子正在从"+sourceTower+"->"+destTower);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值