【JavaSE-6】数组的定义与使用

1、数组的基本概念

1.1、为什么使用数组

为了方便将同一种数据类型的数据进行存储。

1.2、什么是数组

指的是一种容器,可以同来存储同种数据类型的多个值。但是数组容器在存储数据的时候,需要结合隐式转换考虑。如:定义一个int类型的数组,则byte类型,short类型,int类型的数据是可以存到这个数组里面的。

数组可以看成是相同数据类型元素的一个集合。在内存中占用一段连续的内存空间

【数组的特点】

  • 数组中存放的元素数据类型是相同的。
  • 数组所占的内存空间是连续的。
  • 每个内存空间有自己编号,开始位置编号为 0,也就是数组的下标。

1.3、数组的创建和初始化

1.3.1、数组的创建

【语法格式】

数据类型[] 数组名 = new 数据类型[N];
数据类型:表示数组中存放元素的类型
数据类型[]:表示数组的类型
N:表示数组的长度

如:

int[] arr1 = new int[10];//创建一个可以存储10个int类型元素的数组
double[] arr2 = new double[10];//创建一个可以存储10个double类型元素的数组
String[] arr3 = new String[20];//创建一个可以存储20个字符串类型元素的数组

1.3.2、数组的初始化

初始化:也就是在内存中,为数组容器开辟空间,并将数据存入容器中的过程。

数组初始化有动态初始化和静态初始化。

动态初始化:在创建数组时,直接指定数组中元素的个数。由系统给出默认初始值。

数据类型[] 数组名 = new 数据类型[数组的长度];

例子:

int[] arr = new int[10];
String[] arr1 = new String[20];

静态初始化:在创建数组时不直接指定元素个数,而是将具体元素进行指定。

//完整格式
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3,元素4...};

例子:

int[] arr1 = new int[]{0,1,3,4,5,6,7};//数组大小是7
double[] arr2 = new double[]{2.5,3.14,5.69,5.12};//创建的数组大小是4

//简化格式,也就是常用格式
数据类型[] 数组名 = {元素1,元素2,元素3,元素4...};

例子:

int[] array = {1,2,3,4,5};
double[] array = {1.1,1.2,1.3};

【注意事项】

  • 静态初始化编译器在编译时会根据{}中元素个数来确定数组长度。
  • 数组的创建也可以按照 C语言语法格式,但是不推荐:int arr[] = {1,2,3}
  • 如果没对数组进行初始化,数组中元素会有默认值
  • 注意char默认值是'/u0000',打印出来是空格
char[] arr3 = new char[3];
for (int i = 0; i < arr3.length; i++) {
    System.out.print(arr3[i]);
}
System.out.println(1);
//运行结果
//   1
  • 输入数组中存储元素类型为引用类型,默认值是null

【总结】

  • 静态初始化:手动指定数组元素,系统根据元素的个数,计算出数组的长度。
  • 动态初始化:手动指定数组长度,系统给出默认初始化值。

1.4、数组的使用

1.4.1、地址值

int[] arr = {1,2,3,4,5};
System.out.println(arr);

double[] arr2 = {1.1,2.2,3.3};
System.out.println(arr2);

【运行结果】

[I@776ec8df
[D@4eec7777

打印数组的时候,实际出现的是数组的地址值。数组的地址值表示数组在内存中的位置。

[I@776ec8df为例:

[ :表示现在打印的是一个数组。

I:表示现在打印的数组是int类型的。

@:仅仅是一个间隔符号而已。

776ec8df:就是数组在内存中真正的地址值。(十六进制的)

但是,我们习惯性会把[I@776ec8df这个整体称之为数组的地址值。

1.4.2、数组元素访问

可以根据数组下标进行访问,从 0 开始依次递增。

例子:

public static void main(String[] args) {
    int[] arr1 = new int[10];//创建一个可以存储10个int类型元素的数组
    arr1[0] = 1;
    arr1[1] = 3;
    System.out.println(arr1[0]);
    System.out.println(arr1[1]);
    System.out.println(arr1[2]);//int类型数组默认值是0
}

【运行结果】

1
3
0

【注意事项】

  • 数组是一段连续内存空间,支持随机访问,可以通过下标访问任意位置的元素。
  • 访问数组元素时不能越界,否则会报出下标越界异常。

1.4.3、遍历数组

public static void main(String[] args) {
    //遍历数组
    int[] arr1 = {1,2,3,4,5,6};
    for (int i = 0; i < 6; i++) {
        System.out.print(arr1[i] + " ");
    }
}

【运行结果】

1 2 3 4 5 6

虽然此时可以成功访问数组元素,但是前提是因为我们知道了数组元素个数;计算机怎么知道数组元素个数??


在数组中可以通过数组名.length来获取数组的长度。

public static void main(String[] args) {
    //遍历数组

    int[] arr1 = {1,2,3,4,5,6};
    for (int i = 0; i < arr1.length; i++) {
        System.out.print(arr1[i] + " ");
    }
}

【运行结果】

1 2 3 4 5 6

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

public static void main(String[] args) {
    int[] arr = new int[]{1,2,3,4,56,10};
    for (int x:arr) {
        System.out.print(x + " ");
    }
    System.out.println();
    String[] arr1 = new String[]{"hello","world","xaioming","zhangsan"};
    for (String x: arr1) {
        System.out.print(x + " ");
    }
}

【运行结果】

for循环的区别

【扩展】

自动快速生成数组的遍历方法(IDEA提供)

数组名.fori

2、数组是引用类型

2.1、初始 JVM 的内存分布

  • 虚拟机栈:方法运行时使用的内存,比如main方法运行,进入方法栈中执行。每个方法在执行时,都会创建一个栈帧,里面包含:局部变量等信息。方法运行结束后,栈帧就会销毁,栈帧中保存的数据也就被销毁。
  • 堆:存储对象或者数组new来创建的,都是存储在堆内存中。堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还在使用,就不会被销毁。
  • 方法区:存储可以运行的class文件。
  • 本地方法栈:JVM在使用操作系统功能的时候使用,和开发无关。
  • 程序计数器:保存下一条执行的指令地址。

2.2、基本数据类型变量与引用数据类型变量的区别

引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址

例子1:

基本数据类型变量内存:

public static void main(String[] args) {
    int a = 10;
    int b = 10;
    int c = a + b;
}

【内存分析】

例子2:

public static void main(String[] args) {
    int[] arr1 = new int[2];//默认值为0
    System.out.println(arr1);//打印数组arr1在内存中的地址

    int[] arr2 = {1,2,3,4};
    System.out.println(arr2);//打印数组arr2在内存中的地址
}

【运行结果】

[I@776ec8df
[I@4eec7777

【内存分析】

即arr1这个引用指向了数组对象

【总结】

  1. 只要是new出来的一定是在堆中开辟空间
  2. 如果new多次,则在堆中开辟多个空间,每个空间有自己各自的数据
  3. 引用变量并不直接存储对象本身,可以理解为存储的是对象在堆中空间的起始地址。通过该地址,引用变量就可以去操作对象;类比 C语言中的指针

两个数组指向同一个空间情况:

例子3:

public static void main(String[] args) {
    int[] arr1 = {11,22};
    System.out.println(arr1);

    int[] arr2 = arr1;
    System.out.println(arr2);

    System.out.println(arr1[0]);
    arr1[1] = 10;
    System.out.println(arr1[1]);
    System.out.println(arr2[1]);
}

【运行结果】

[I@776ec8df
[I@776ec8df
11
10
10

【内存分析】

2.3、认识null

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

null 的作用类似于 C 语言中的 NULL (空指针), 都是表示一个无效的内存位置. 因此不能对这个内存进行任何读写操作. 一旦尝试读写, 就会抛出 NullPointerException.

例子 :

【运行结果】

会报错!!

Exception in thread "main" java.lang.NullPointerException: Cannot read the array length because "arr" is null
	at Test01.main(Test01.java:16)

3、数组的应用场景

3.1、保存数据

可以通过数组将相同数据类型的数据进行保存,之后可以通过数组遍历进行访问数据。

3.2、作为函数的参数

例子

public static void func1(int[] arr){
    arr = new int[10];
}
public static void func2(int[] arr){
    arr[0] = 19;
}
public static void main(String[] args) {
    int[] arr1 = {1,2,3,4};
    func1(arr1);
    for (int i = 0; i < arr1.length; i++) {
        System.out.print(arr1[i] + " ");
    }
    System.out.println();
    int[] arr2 = {1,2,3,4};
    func2(arr2);
    for (int i = 0; i < arr2.length; i++) {
        System.out.print(arr2[i] + " ");
    }
}

func2是改变原来指向的对象的值,而func1是改变所指向的对象


函数func1内存分析:

之后执行函数func1中的代码arr = new int[10];,这样就相当于在堆中新创建一片内存空间,然后将栈中arr存储的值进行修改。如下所示:

这样在执行main函数的for循环时,func1创建的栈帧和在堆中的变量空间都会被销毁,接下来打印输出的arr1操作的数据还是原来的数据,所以输出结果是1 2 3 4


函数func2内存分析:

即没有修改数组的引用,这样在函数func2的函数栈帧中,数组arrmain函数中的数组指向的是同一片堆内存空间,所以修改数据之后,在main函数进行调用时也会发生改变。如下所示:

所以在main函数的for循环时,打印输出的数组值改为19 2 3 4

【总结】

所谓的“引用”本质上就是存了一个地址。Java 将数组设定为引用类型,在后续将数组作为参数进行传参时,其实只是将数组地址传入到函数形参中,这样可以避免对整个数组的拷贝。

3.3、作为函数的返回值

例子:

public static int[] func3(){
    //方式1
    /*int[] arr = {1,2,3,4};
      return arr;*/
    
    //方式2
    return new int[]{1,2,3,4};
}
public static void main(String[] args) {
    int[] ret = func3();
    for (int i = 0; i < ret.length; i++) {
        System.out.print(ret[i] + " ");
    }
}

【内存分析】

【结论】

new出来的对象一定是创建在堆上,同时在栈中局部变量里存储的是在堆中的地址值。


3.4、Arrays工具类

可以借助工具类进行输出:import java.util.Arrays;

以下方法构成方法的重载

利用toString()可以接收数组,然后将其转换成字符串,这样就可以方便输出,而不是在用for循环遍历数组。

import java.util.Arrays;
public static int[] func3(){
    /*int[] arr = {1,2,3,4};
        return arr;*/
    return new int[]{1,2,3,4};
}
public static void main(String[] args) {
    int[] ret = func3();
    String s = Arrays.toString(ret); 
    System.out.println(s);
}

【运行结果】

[1,2,3,4]

4、数组练习(熟悉 Arrays工具类)

4.1、数组转字符串

  1. 使用import java.util.Arrays;利用Arrays中的toString方法可以实现
  2. 自行实现toString方法:
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[] array = {1,2,3,4,5};
    String ret = myToString(array);
    System.out.println(ret);
}

【运行结果】

[1,2,3,4,5]

4.2、数组拷贝

将一个数组元素拷贝到另一个数组中的操作

方法1

public static void main(String[] args) {
    int[] arr = {1,2,3,4,5};
    //拷贝数组元素到另一个数组中
    int[] copy = new int[arr.length];
    for (int i = 0; i < arr.length; i++) {
        copy[i] = arr[i];
    }
    System.out.println(Arrays.toString(copy));//遍历输出数组copy的元素 [1,2,3,4,5]
}

【注意】

以下情况不是拷贝!

int[] arr = {1,2,3,4,5};
int[] newArr = arr;
System.out.println(Arrays.toString(newArr));

【内存分析】


方法2

利用Arrays工具类进行拷贝:

语法:Arrays.copyOf(要拷贝的数组名,要拷贝的数组大小)

public static void main(String[] args) {
    int[] arr = {1,2,3,4,5};
    //利用数组类进行拷贝
    int[] copy = Arrays.copyOf(arr,arr.length);
    //利用数组类将数组输出
    System.out.println(Arrays.toString(copy));//[1,2,3,4,5]
}

如果将int[] copy = Arrays.copyOf(arr,arr.length);改成int[] copy = Arrays.copyOf(arr,arr.length * 2);将相当于数组扩容了:

如果想拷贝数组中的一部分元素,可以使用Arrays中的Arrays.copyOfRange()

//其中的from,to表示数组下标,范围是[from,to)
int[] copy1 = Arrays.copyOfRange(arr,0,3);
System.out.println(Arrays.toString(copy1));

【运行结果】


方法3

利用Systemarraycopy拷贝:

语法分析:

native表示的是本地方法

例子:

//利用System.arraycopy方法
int[] arr = {1,2,3,4,5};
int[] copy2 = new int[arr.length * 2];
//将数组arr从下标为1的位置开始拷贝到copy2数组的下标位置为2开始的位置,拷贝的长度是4
System.arraycopy(arr,1,copy2,2,arr.length-1);
System.out.println(Arrays.toString(copy2));//[0, 0, 2, 3, 4, 5, 0, 0, 0, 0]

4.3、求数组元素的平均值

//给定一个整型数组, 求平均值
public static double avg(int[] arr){
    double sum = 0;
    for (int i = 0; i < arr.length; i++) {
        sum += arr[i];
    }
    return sum / arr.length;
}
public static void main(String[] args) {
    //给定一个整型数组, 求平均值
    int[] arr = new int[]{1,2,3,4,5};
    System.out.println(avg(arr));//3.0
}

4.4、查找数组中指定元素(顺序查找)

public static int findVal(int[] arr,int key){
    for (int i = 0; i < arr.length; i++) {
        if(arr[i] == key){
            return i;
        }
    }
    return -1;//表示没有找到
}
public static void main(String[] args) {
    int[] arr = {1,3,5,7,9};
    int ret = findVal(arr,10);
    if(ret != -1){
        System.out.println("找到了,下标是:" + ret);
    }else {
        System.out.println("没有找到");
    }
}

4.5、查找数组中指定元素(二分查找)

二分查找针对的是有序数组,在 Java 中可以利用Arrays工具类中的方法Arrays.sort()将无序数组从小到大进行排列。

//二分查找
public static int binarySearch(int[] arr,int key){
    int left = 0;
    int right = arr.length-1;
    while(left <= right){
        int mid = (left + right) / 2;
        if(key > arr[mid]){
            left = mid + 1;
        } else if (key < arr[mid]) {
            right = mid - 1;
        }else {
            return mid;//返回目标值在数组的下标
        }
    }
    return -1;//表示没有找到
}

利用Arrays工具类中的方法Arrays.binarySearch()可以直接查找


Arrays工具类方法的补充

  • 判断两个数组对应位置上的元素是否相等Arrays.equals(arr1,arr2);
  • 把指定数据直接填充到数组的所有位置Arrays.fill(arr,key)
  • 将指定数据填充到数组的指定位置Arrays.fill(arr,from,to,key),区间是左闭右开[from,to)

4.6、数组排序(冒泡排序)

按照从小到大顺序排序

//冒泡排序
public static void bubbleSort(int[] arr){
  for (int i = 0; i < arr.length - 1; i++) {//趟数
    boolean flag = true;//初始表示没有发生交换
    for (int j = 0; j < arr.length-1-i; j++) {//j表示每趟比较的次数
      if(arr[j] >= arr[j+1]){
        int tmp = arr[j];
        arr[j] = arr[j+1];
        arr[j+1] = tmp;
        flag = false;//发生数据交换
      }
    }
    //一趟下来不发生交换说明已经有序
    if(flag){
      break;
    }
  }
}

【运行结果】

4.7、数组逆序

public class Test01 {
    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--;
        }
    }
}

4.8、打乱数据

定义一个数组,存入1~5。要求打乱数组中所有数据的顺序。

代码示例:

//1.定义数组存储1~5
int[] arr = {1, 2, 3, 4, 5};
//2.循环遍历数组,从0索引开始打乱数据的顺序
Random r = new Random();
for (int i = 0; i < arr.length; i++) {
    //生成一个随机索引
    int randomIndex = r.nextInt(arr.length);//随机数范围[0,5)
    //拿着随机索引指向的元素 跟 i 指向的元素进行交换
    int temp = arr[i];
    arr[i] = arr[randomIndex];
    arr[randomIndex] = temp;
}
//3.当循环结束之后,那么数组中所有的数据已经打乱顺序了
for (int i = 0; i < arr.length; i++) {
    System.out.print(arr[i] + " ");
}

5、二维数组

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

基本语法:

数据类型[][] 数组名 = new 数据类型[行数][列数]{初始化数据};

举例:

//二维数组的创建
int[][] arr1 = {{1,2,3},{4,5,6}};//表示2行3列
int[][] arr2 = new int[][]{{1,2,3},{4,5,6}};
int[][] arr3 = new int[2][3];//没有赋初始值,则默认都为0

二维数组的遍历:

//二维数组的遍历
int[][] arr1 = {{1,2,3},{4,5,6}};//表示2行3列
for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
        System.out.print(arr1[i][j] + " ");
    }
    System.out.println();
}

二维数组的本质分析:

所以我们可以直接求出二维数组的行和列:

//如何确定行和列?
//二维数组其实就是一维数组,只不过存储的元素是地址,对应的是另一个一维数组
int[][] arr1 = {{1,2,3},{4,5,6}};//表示2行3列
for (int i = 0; i < arr1.length; i++) {
    //arr1.length计算的是数组的元素个数,{{1,2,3},{4,5,6}},也就是有两个元素{1,2,3},{4,5,6}
    for (int j = 0; j < arr1[i].length; j++) {
        //arr1[0].length 表示的是元素{1,2,3}的长度,也就是第一行的长度...
        System.out.print(arr1[i][j] + " ");
    }
    System.out.println();

}

可以利用Arrays工具类对二维数组进行遍历:Arrays.deepToString()


特殊情况:不规则的二维数组:二维数组可以不规定列数,但是也可以将其进行遍历

画图分析:

情况 1:

//可以不指定列数,而C语言中需要指定列数
int[][] arr1 = new int[2][];
System.out.println(Arrays.deepToString(arr1));

【运行结果】

[null, null]

情况2

int[][] arr1 = new int[2][];
for (int i = 0; i < arr1.length; i++) {
    for (int j = 0; j < arr1[i].length; j++) {
        System.out.print(arr1[i][j] + " ");
    }
    System.out.println();
}

【运行结果】

Exception in thread "main" java.lang.NullPointerException: Cannot read the array length because "arr1[i]" is null
at Test01.main(Test01.java:44)

原因:

因为创建一一个二维数组arr1的具体情况是如下所示,此时没有赋值,则默认是null

对于情况 2,报错的原因在于arr1[i].length,因为对于null的数组,不可以进行任何读写操作,所以会报出错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿宾爱干饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值