目录
什么是数组
数组的本质就是让我们能批量创建相同的类型的变量
举例
比如我们创建两个整型变量,可以用int a,int b,但是我们要是想创建1w个整型变量,我们就需要使用数组这种数据结构来实现
注意点
- 数组是一种顺序表
- 在JAVA中,数组包含的变量必须是相同类型
数组的创建和初始化
两种创建方式
1动态初始化
2静态初始化(采用了语法糖,javac编译之后,就是动态初始化)
注意点
数组定义的时候,其长度可以是一个变量,在C语言中是不行的
数组的使用
获取数组的长度
- 使用arr.length能获取arr数组的长度,.是成员访问操作符
- [ ]是按下标取数组元素,需要注意,下标是从0开始,其实下标就是这个元素距离第一个元素的偏移量
- [ ] 不仅能访问数组元素,并且还能修改数组的元素
- 下标访问操作不能超出有效范围[0,arr.length-1],不然会出现下标越界异常
- 数组名保存的是数组首元素的地址,这样我们只要知道其他元素相较于第一个元素的距离就能找到,这也[ ]的工作原理
两种遍历数组的方法
- 第一个遍历,i只是表示数组每个元素的索引下标,arr[i]确确实实拿到每个数组的元素
- 第二个遍历,其中的i是表示从数组的第一个元素开始取值,第一次把第一个元素的值复制给一份给i,第二次把循环把第二个元素的值复制一份给i,依次类推,直到整个数组遍历结束,有点相当于实参和形参的关系
- for-each不能修改数组的内容,只能访问
public static void main(String[] args) { int []arr=new int[]{1,2,3,4,5,6}; int []arr1=new int[]{1,2,3,4,5,6}; for (int i = 0; i < arr.length; i++) { System.out.print(arr[i]+" "); } System.out.println("修改后"); for (int i = 0; i < arr.length; i++) { arr[i]=1; } for (int i = 0; i < arr.length; i++) { System.out.print(arr[i]+" "); } System.out.println(); //jdk1.5引入了for-each循环,增强型for循环 for (int i:arr1){ System.out.print(i+" "); } System.out.println("修改后"); for(int i:arr1){ i=1; } for (int i:arr1){ System.out.print(i+" "); } }
数组作为方法的参数
在前面,我们知道如果交换两个数值型变量,是不能交换成功的,但是用数组可以交换成功,今天我们就来具体探究一下
public static void main(String[] args) { int a=10; int b=20; swap(a,b); System.out.println("a="+a+" b="+b);//a=10 b=20 int []arr=new int[]{10,20}; swap(arr); System.out.println("arr[0]="+arr[0]+" arr[1]="+arr[1]); //arr[0]=20 arr[1]=10 } public static void swap(int a,int b){ int temp=a; a=b; b=temp; }public static void swap(int []arr){ int temp=arr[0]; arr[0]=arr[1]; arr[1]=temp; }
为什么会这样,我们首先要了解JVM中内存分类,JVM将内存分为6个区域,现在只讲栈区和堆区
- 方法的调用就是在栈区进行的,每个方法的调用,都会伴随着入栈和出栈的过程(栈是一个先进后出的数据结构)
- 并且方法中的局部变量和形参都在栈中存储,并且随着方法的调用结束,临时变量都会被销毁
- 还有一个内存区叫做堆区,所有的对象都在堆区存储,比如数组对象,类的实例化对象,接口的对象
- int [] arr=new int[5];这个arr是一个引用,引用就是将这个对象起一个别名,保存的数值是这个对象的地址,对于数组来说,arr保存的就是这个长度为5的数组对象的首元素的地址
- 在JAVA中看到new,就一定会在堆中开辟一块新的内存空间
- JAVA中所有对象内存空间的释放都是由JVM决定,程序员无权干涉,只要当一个对象没有任何强引用指向,且当前JVM内存不够用时,才会释放这个对象的内存
为什么swap不能交换两个局部变量的详解
为什么数组这个引用类型可以成功交换
为什么下面一段代码也不能交换成功
public static void main(String[] args) { int []arr=new int[]{10,20}; swap(arr); System.out.println("arr[0]="+arr[0]+" arr[1]"+arr[1]); } public static void swap(int []arr){ arr=new int[]{10,20}; int temp=arr[0]; arr[0]=arr[1]; arr[1]=temp; }
认识null
- null在JAVA中表示空引用,表示一个无效的引用
- null类似C语言中的空指针,都表示一个无效的的内存位置因此不能对这个内存进行任何读写操作
- JAVA中null和0号地址没有任何关联
数组一些常用的方法和练习
- 大家以后看到JDK的某些类,在某些类后面加S,表示这种类都是工具类,提供了大量的有用的方法,可以直接调用
- Arrays--数组的工具类,包含了数组转换字符串的方法,数组排序的方法等等操作
数组转换为字符串
Arrays的toString方法
public static void main(String[] args) { int []arr={1,2,3,4,6,6}; String str= Arrays.toString(arr); System.out.println(str); //输出[1,2,3,4,6,6] }
数组的拷贝
Arrays的copyOf和copyOfRange
public static void main(String[] args) { int[] arr={1,2,3,4,5,6,7,8}; int[] arr1=Arrays.copyOf(arr,3); System.out.println(Arrays.toString(arr)); System.out.println(Arrays.toString(arr1));//[1,2,3] int[] arr2=Arrays.copyOfRange(arr,2,4); System.out.println(Arrays.toString(arr2));//[3,4] }
- 对于copyOf,1如果新数组的长度小于原数组长度,部分拷贝。从第一个开始拷贝,直到到达新的数组长度 2如果新数组的长度等于原数组的长度 全拷贝 3新数组的长度大于原数组的长度,全拷贝,后面剩余的元素,用默认值填充
- 对于copyOfRange,它的区间是前闭后开的[2,4)
数组的排序
有七大排序算法,我们先介绍冒泡排序,默认是排升序
原理:将数组分为两个区间,一个为排序好的(初始为0),另一个为待排序的(初始为[0...n],每次将待排序中最大值,放入排序好的区间,每次排序好的区间长度增加1,待排序的减少1,直到待排序的长度为0,就不需要排序了
假如需要排序的数组为 5 4 3 2 1 待排序区间为5 4 3 2 1已排序区间为空
- 待排序 4 3 2 1 已排序 5
- 待排序 3 2 1 已排序 4 5
- 待排序 2 1 已排序 3 4 5
- 待排序 1 已排序 2 3 4 5
- 待排序 已排序 1 2 3 4 5
代码实现
public static void main(String[] args) { int []arr={1,3,4,1,2,9,112,3,42,212,5}; sortArr(arr); System.out.println(Arrays.toString(arr)); } public static void sortArr(int []arr){ for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr.length-1-i; j++) { 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={1,3,4,1,2,9,112,3,42,212,5}; sortArr(arr); System.out.println(Arrays.toString(arr)); } public static boolean judgeSort(int[] arr){ for (int i = 0; i < arr.length-1; i++) { if (arr[i]>arr[i+1]) { return false; } } return true; } public static void sortArr(int []arr){ for (int i = 0; i < arr.length-1; i++) { if (judgeSort(arr)){ break; } for (int j = 0; j < arr.length-1-i; j++) { if (arr[j]>arr[j+1]){ int tmp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=tmp; } } } }
数组逆序
原理:将需要逆序的数组分为两个区间,一个为已经逆序的区间,另一个为未逆序的区间,每次将未逆序的区间的最后一个元素和第一个元素交换,直到未逆序的区间为空或者只剩一个数据
例子
将2 3 4 1 6 7 逆序 已经逆序的区域为空 未逆序的空间为 2 3 4 1 6 7
- 已经逆序的区间7 2 未逆序的区域 3 4 1 6
- 已经逆序的区间7 6 3 2 未逆序的区域 4 1
- 已经逆序的区间7 6 1 4 3 2 未逆序的区域 空
代码实现
public static void main(String[] args) { int []arr={1,2,3,4,5,6,7}; reserve(arr); System.out.println(Arrays.toString(arr)); } public static void reserve(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--; } }
数组的数字排列问题
怎么将一个数组的奇数位放到前面,偶数都放在后面
从前往后找到第一个偶数,从后往前找到第一个奇数,将其交换,前面的指针不能大于后面的指针
public static void main(String[] args) { int []arr={1,2,9,5,3,4,2,6}; transform(arr); System.out.println(Arrays.toString(arr)); } public static void transform(int []arr){ int left=0; int right=arr.length-1; while(left<right){ while (left<right&&arr[left]%2==0){ left++; } while (left<right&&arr[right]%2!=0){ right--; } int tmp=arr[left]; arr[left]=arr[right]; arr[right]=tmp; left++; right--; } }
二维数组
基本语法
数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };
- 在JAVA中二维数组也是连续存储的,跟C没什么区别
- 数组名存储的是二维数组的首元素地址,也是第一行数组的地址
- 基本不怎么用,了解就ok