目录
一.数组的基本用法
1.什么是数组
数组:储存一组相同数据类型的数据的集合
本质:就是让我们能 "批量" 创建相同类型的变量
举例:比如我们创建一个整型变量可以使用int a = 1;那么如果创建多个整型变量就可以使用数组int [] array = {...}
注意事项:需要注意的是,在Java中,数组储存的变量必须是同一类型!!
2.创建数组
动态初始化
基本语法:数据类型[] 数组名称 = new 数据类型 [] { 初始化数据 };
接下来举个例子来深入理解一下:
public class TestDemo {
public static void main(String[] args) {
int[] array = new int[]{1,2,3,4,5};
}
}
静态初始化
基本语法:数据类型[] 数组名称 = { 初始化数据 };
同样的,我们来举个例子:
public class TestDemo {
public static void main(String[] args) {
int[] array2 = {1,2,3,4,5,6};
}
}
需要注意的是: 静态初始化的时候, 数组元素个数和初始化数据的格式是一致的
3.数组的基本使用
获取数组长度
public class TestDemo {
public static void main(String[] args) {
int[] array = new int[]{1,2,3,4,5};
System.out.println("length:"+array.length);
}
}
使用 array.length 能够获取到数组的长度," . " 这个操作为成员访问操作符,后面在面向对象中会经常用到
访问数组元素
public class TestDemo {
public static void main(String[] args) {
int[] array = new int[]{1,2,3,4,5};
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]);
}
}
1.使用 [ ] 按下标取数组元素,需要注意的是,下标从 0 开始计数,所以下标访问操作不能超出有效范围 [0, length - 1] ,如果超出有效范围,会出现下标越界异常
2. 使用 [ ] 操作既能读取数据, 也能修改数据
遍历数组元素
法一:使用for循环遍历数组元素
public class Demo {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
for (int i = 0; i <= array.length-1 ; i++) {
System.out.print(array[i]+" ");
}
}
}
法二:使用for each循环遍历数组元素
for each循环:又称增强for循环,是 for 循环的另外一种使用方式,能够更方便的完成对数组的遍历,可以避免循环条件和更新语句写错。但是for each循环是拿不到下标的,用在集合中比较多
public class Demo {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
for (int x : array) {
System.out.print(x+" ");
}
}
}
以字符串打印数组
如果我们要打印一个数组,除了写一个打印方法外还能怎么打印呢?首先我们来试一下直接打印,打印出来是:[I@1b6d3586,这显然不是我们想要的。直接打印行不通,这时候我们就需要借助Java的操作数组的工具类Arrays.toString,将参数的数组以字符串的形式进行输出
import java.util.Arrays;//导入数组工具类包
public class Demo {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
System.out.println(Arrays.toString(array));
}
}
二.数组作为方法的参数
1.基本用法
public class Demo {
public static void printArray(int[] a){
for (int x : a) {
System.out.print(x+" ");
}
}
public static void main(String[] args) {
int [] array = {1,2,3,4,5};
printArray(array);
}
}
在这里,你可以看到int [] a是形参,而int [] array是实参
2.理解引用类型
传内置类型
public class Demo2 {
public static void func(int x){
x = 10;
System.out.println("x:"+x);
}
public static void main(String[] args) {
int num = 20;
func(num);
System.out.println("num:"+num);
}
}
我们发现,修改形参x的值,并不影响实参num的值!
传引用类型
//以传输组为例
public class Demo3 {
public static void exchange(int[] array){
int temp = array[0];
array[0] = array[1];
array[1] = temp;
}
public static void main(String[] args) {
int[] array ={1,2,3,4,5};
System.out.println("交换前:");
System.out.println("array[0]="+array[0]+",array[1]="+array[1]);
exchange(array);
System.out.println("交换后:");
System.out.println("array[0]="+array[0]+",array[1]="+array[1]);
}
}
通过这个运行结果我们可以发现,以数组为参数传参就可以修改变量的值,这是为什么呢?
1.原来,此时的array是一个引用变量,里面储存的是变量的地址。
2.引用指向的是对象,前面提到了对象储存在堆上面!Java中是能拿到堆上的地址的!
接下来再通过图来具体分析一下来帮助理解:
关于引用的注意事项:
1.引用一定在栈上吗?这取决与变量的性质,如果是局部变量,就一定在栈上,如果是实例化成员变量就不一定了
2.一个引用能同时指向多个对象吗?不能!一个引用只能有一个对象!
3. 认识null
null:在 Java 中表示 "空引用" , 也就是一个无效的引用
接下来通过代码让大家具体感受一下
public class Demo4 {
public static void exchange(int[] array){
int temp = array[0];
array[0] = array[1];
array[1] = temp;
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
exchange(null);
}
}
代码运行时报错了,我们来具体分析一下报错原因以及解决方案
在传引用的时候null会报出空指针异常,null的作用类似于C语言中的null,都表示一个无效的内存位置,因此不能进行任何读取操作
4.初识 JVM 内存区域划分
接下来一张图带你详细了解JVM区域划分
这里补充一下Native 方法: JVM 是一个基于 C++ 实现的程序。在 Java 程序执行过程中,本质上也需要调用 C++ 提供的一些函数进行和操作系统底层进行一些交互。因此在 Java 开发中也会调用到一些 C++ 实现的函数。这里的 Native 方法就是指这些 C++ 实现的,再由Java 来调用的函数
三.数组作为方法的返回值
我们来写一个方法将数组元素都乘以2
import java.util.Arrays;
public class Demo4 {
public static void transform(int[] array) {
for (int i = 0; i < array.length; i++) {
array[i] = array[i] * 2;
}
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
transform(array);
System.out.println(Arrays.toString(array));
}
}
这样写没有任何问题,但是会破坏原来数组,如果我们不希望破坏原来数组,应该怎样写呢?这时候需要在方法内部创建一个新的数组,并且返回该数组,接下来看看具体的代码实现
import java.util.Arrays;
public class Demo4 {
public static int[] transform(int[] array) {
int[] array2 = new int[array.length];
for (int i = 0; i < array.length; i++) {
array2[i]=2*array[i];
}
return array2;
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] ret = transform(array);//创建一个数组来接收array2
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(ret));
}
}
这样做就可以不破坏原来数组啦!同时,由于数组是引用类型,返回的时候只是将这个数组的首地址返回给函数调用者, 没有拷贝数组内容, 从而比较高效
四.数组的拷贝
1.for循环进行拷贝
import java.util.Arrays;
public class Demo5 {
public static int[] copyArray(int[] array){
int[] copy = new int[array.length];//定义一个数组的大小
for (int i = 0; i < array.length; i++) {
copy[i] = array[i];
}
return copy;
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
System.out.println(Arrays.toString(copyArray(array)));
}
}
2.Arrays.copyOf进行拷贝
我们通过帮助手册来了解一下copyOf的作用和使用方法:copyOf(需要拷贝的数组,拷贝的长度)
接下来通过代码实现
import java.util.Arrays;
public class Demo5 {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] ret = Arrays.copyOf(array,array.length);
System.out.println(Arrays.toString(ret));
}
}
3. Arrays.copyOfRange进行拷贝
通过Arrays.copyOfRange拷贝局部
public class Demo5 {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] ret = Arrays.copyOfRange(array,0,5);
System.out.println(Arrays.toString(ret));
}
}
相信看到 下标为5还不越界 这里一定有些懵, 这是因为Java的from和to都是左闭右开的,这里的to 5相当于<5,那就是取到4这里!
4.通过System.arraycopy进行拷贝
在使用Arrays.copyOf进行拷贝的时候,我们将光标移动到Arrays.copyOf上,按下ctrl键,可以得到
发现Arrays.copyOf调用了System.arraycopy,我们按住ctrl,点击System.arraycopy分析一下用法
import java.util.Arrays;
public class Demo5 {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] copy = new int[array.length];
System.arraycopy(array,0,copy,0,array.length);
System.out.println(Arrays.toString(copy));
}
}
5.通过array.clone()进行拷贝
.clone()会产生一个副本,里面的内容和原来的数组一模一样
import java.util.Arrays;
public class Demo6 {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] copy = array.clone();
System.out.println(Arrays.toString(copy));
}
}
6.深拷贝和浅拷贝
首先来了解一下这俩的概念:
深拷贝:修改拷贝后的数组不会影响到原数组
浅拷贝:修改拷贝后的数组会影响到原数组
总结:想要达到深拷贝,需要对对象本身进行拷贝。刚刚我们所举得例子全是深拷贝。
如果数组放的是基本数据类型,就是深拷贝。如果数组放的是引用类型那就是浅拷贝。
五.数组经典习题
温故而知新,接下来提供一些经典习题供读者练习,同时附上笔者解法,如有错误,请斧正!
1.查找最大元素
给定一个整型数组, 找到其中的最大元素 (找最小元素同理)
public class Test1 {
public static void findMax(int[] array){
int max = array[0];//从0下标开始寻找
for (int i = 0; i < array.length; i++) {
if(array[i]>max){
max = array[i];
}
}
System.out.println(max);
}
public static void main(String[] args) {
int[] array = {1,5,7,9,11};
findMax(array);
}
}
2.求数组中元素的平均值
给定一个整型数组, 求平均值
public class Test2 {
public static double average(int[] array){
int sum = 0;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
double num = (double)sum/(double)array.length;
return num;
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
System.out.println(average(array));
}
}
3.查找数组中指定元素
给定一个数组, 再给定一个元素, 找出该元素在数组中的位置.
顺序查找
public class Test3 {
public static int find(int[] array,int toFind){
for (int i = 0; i < array.length; i++) {
if (array[i] == toFind){
return i;
}
}
return -1;//表示未找到
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6};
System.out.println(find(array,5));
}
}
二分查找
二分查找有个缺陷,就是只能查找全是升序或者降序的数组,接下来以升序为例
public class Test4 {
public static int binarySearch(int[] array,int toFind){
int left = 0;
int right = array.length-1;
while(left<=right){
int mid =(right+left)/2;
if(toFind>array[mid]){//在右半区去找,并且缩小范围
left = mid+1;
}else if(toFind<array[mid]){//在左半区去找,并且缩小范围
right = mid-1;
}else{
return mid;
}
}
return -1;
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6};
System.out.println(binarySearch(array,2));
}
}
4.检查数组的有序性
给定一个整型数组, 判断是否该数组是有序的(升序)
public class Test5 {
public static boolean func(int[] array){
for (int i = 0; i < array.length; i++) {
if(array[i]<array[i+1]){
return true;
}
}
return false;
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
System.out.println(func(array));
}
}
5.数组排序
给定一个数组, 让数组升序 (降序) 排序
冒泡排序
import java.util.Arrays;
public class Test6 {
public static void bubbleSort(int[] array){
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length-1-i ; j++) {
if(array[j]>array[j+1]){
int temp = array[j+1];
array[j+1] = array[j];
array[j] = temp;
}
}
}
}
public static void main(String[] args) {
int[] array = {1,8,9,5,4};
bubbleSort(array);
System.out.println(Arrays.toString(array));
}
}
Arrays.sort
冒泡排序性能较低. Java 中内置了更高效的排序算法:Array.sort
import java.util.Arrays;
public static void main(String[] args) {
int[] array = {1,8,9,5,4};
Arrays.sort(array);
System.out.println(Arrays.toString(array));
}
}
6.数组逆序
给定一个数组, 将里面的元素逆序排列
思路:给定两个下标分别指向第一个元素和最后一个元素,让这两个元素交换,然后再实现自增和自减,重复该操作,直到走到数组的最中间
import java.util.Arrays;
public class Test7 {
public static void reverse(int[] array){
int left = 0;
int right = array.length-1;
while(left < right){
int temp = array[left];
array[left] = array[right];
array[right] = temp;
left++;
right--;
}
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
reverse(array);
System.out.println(Arrays.toString(array));
}
}
7.数组数字排列
给定一个整型数组, 将所有的偶数放在前半部分, 将所有的奇数放在数组后半部分
基本思路:设定两个下标分别指向第一个元素和最后一个元素。 用前一个下标从左往右找到第一个奇数,用后一个下标从右往左找到第一个偶数,然后交换两个位置的元素. 依次循环即可
import java.util.Arrays;
public class Test8 {
public static void transform(int[] array){
int left = 0;
int right = array.length-1;
while(left < right){
while(left<right && array[left]%2==0){
left++;//如果是偶数,则去判断下一个元素
}
while(left<right && array[right]%2!=0){
right--;//如果是奇数,则去判断下一个元素
}
//交换奇数和偶数
int temp = array[left];
array[left] = array[right];
array[right] = temp;
}
}
public static void main(String[] args) {
int[] array = {1,2,6,8,9,7};
transform(array);
System.out.println(Arrays.toString(array));
}
}
六.二维数组
1.基本语法
数据类型[ ][ ] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };
Java中二维数组可以省略列,不能省略行
int[][]array = {{1,2,},{3,4},{5,6}};
int[][]array2 = new int[3][2];
int[][]array3 = new int[][]{{1,2,},{3,4},{5,6}};
2.代码示例
public class TestDemo {
public static void main(String[] args) {
int[][] arr = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.printf("%d\t", arr[i][j]);
}
System.out.println("");
}
}
}
3.具体用法
二维数组的具体用法与一维数组类似,这里不再赘述