当前位置:首页 » 《随便一记》 » 正文

Java:数组的定义和使用(万字解析)

23 人参与  2024年11月17日 14:41  分类 : 《随便一记》  评论

点击全文阅读


目录

1. 数组的概念

2. 数组的基础知识

2.1 数组的创建

\1. 基础创建格式:

\2. 类似C语言的创建格式:

【错误的创建(初始化)格式】

2.2 数组的数据类型

2.3 数组的初始化 —— 两种方式

\1.动态初始化:(完全默认初始化)

\2. 静态初始化:

【注意事项】

3. 数组的使用

3.1 数组元素的下标访问

3.2 计算数组长度

3.3 循环遍历数组的2种方式

\1. for语句

\2. for - each语句

【两种遍历方式的区别】

4. 数组是引用类型(内存空间的底层知识)

4.1 JVM的内存区域划分

4.2 内存分配:基础类型的变量 和 引用类型的变量

4.3 数组大小可变吗?

4.4 “数组1 = 数组2”会发生什么?

4.5 空引用null

5. 数组 与 方法

5.1 数组作为方法参数

5.2 数组作为方法返回类型

6. Arrays的一些实用工具方法

6.1 数组转字符串

6.2 数组的拷贝

6.3 数组的排序

6.4 二分查找指定元素

6.5 判断两个数组是否相等

6.6 填充数组元素


1. 数组的概念

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

1. 数组中存放的元素其类型相同。

2. 数组的空间是连在一起的。

3. 每个空间有自己的编号,起始位置的编号为0,即数组的下标。

2. 数组的基础知识

首先要明确一点,数组在Java中属于引用类型,引用类型变量的创建都需要new出来。

2.1 数组的创建

\1. 基础创建格式:

T[ ] 数组名 = new T[N]; 

T:表示数组中存放元素的类型T[ ]:表示数组的类型N:表示数组的长度

例如:

int[] array1 = new int[10];       // 创建一个可以容纳10个int类型元素的数组double[] array2 = new double[5];  // 创建一个可以容纳5个double类型元素的数组String[] array3 = new String[3];  // 创建一个可以容纳3个字符串元素的数组

数组的创建也可以类似C语言那样,把方括号[]写在变量名的右边。

\2. 类似C语言的创建格式:

T 数组名[ ] = new T[N];

T:表示数组中存放元素的类型T[ ]:表示数组的类型N:表示数组的长度

例如:

int array1[] = new int[10]; // 创建一个可以容纳10int类型元素的数组double array2[] = new double[5]; // 创建一个可以容纳5double类型元素的数组String array3[] = new String[3]; // 创建一个可以容纳3个字符串元素的数组

【错误的创建(初始化)格式】

没有new一个数组对象。将数组长度的定义写在了第一个方括号[ ]里面。

例如:

2.2 数组的数据类型

注意:java中数组的数据类型是T[],而不是T[n]。

【在C语言中,数组的数据类型是T[n]】

比如现在有1个元素个数为3的整型数组“int[] arr = new int[3]”,那么该数组arr的数据类型是int[ ],而不是int[3]。

2.3 数组的初始化 —— 两种方式

数组的初始化主要分为动态初始化以及静态初始化

\1.动态初始化(完全默认初始化)

在创建数组时,直接指定数组中元素的个数N。创建后,从0到N-1的数组元素都被默认初始化。

语法格式:T[ ] 数组名 = new T[N];

例如:int[] array = new int[10];

如果数组中存储元素类型为基础类型,默认值为基础数据类型对应的默认值,比如:

\u 是用来表示Unicode转义字符的前缀。它的格式为 \u 后面跟着四个十六进制数字,用于表示一个特定的Unicode字符。

例如:\u0041 表示字符 A,因为 0041A 的Unicode编码。

如果数组中存储元素类型为引用类型,默认值为null

\2. 静态初始化:

在创建数组时不直接指定数据元素个数,而直接将具体的数据内容进行指定

(完全格式)语法格式①:T[ ] 数组名 = new T[ ]{data1, data2, data3, ..., datan};   (左边也可以写成“T 数组名[ ]”)

(省略格式)语法格式②:T[ ] 数组名 = {data1, data2, data3, ..., datan};    (左边也可以写成“T 数组名[ ]”)

【这写法类似C语言的数组完全初始化,虽然看上去省去了new T[ ],但是编译器编译代码时还是会还原

例如:

格式1的静态初始化:

int[] array1 = new int[]{0,1,2,3,4,5,6,7,8,9};String[] array3 = new String[]{"hell", "Java", "!!!"};

格式2的静态初始化:

int[] array1 = {0,1,2,3,4,5,6,7,8,9};double[] array2 = {1.0, 2.0, 3.0, 4.0, 5.0};String[] array3 = {"hell", "Java", "!!!"};

【注意事项】

1.静态和动态初始化也可以分为两步,但是省略格式不可以。【类似C语言的初始化格式不能分成两步,而且这在C语言中也是不可以的】
int[] array1; array1 = new int[10]; int[] array2; array2 = new int[]{10, 20, 30}; // 注意省略格式不可以拆分, 否则编译失败// int[] array3; // array3 = {1, 2, 3};
2. 在使用省略格式的静态初始化时,浮点数数组不能用float[ ]类型接收,整数无限制。(所以不太建议使用省略格式)

3. 数组的初始化不能既是动态初始化,又是静态初始化。(同时也说明:Java中不能像C语言那样不完全初始化。)

这里既直接指定了数组的大小是5(动态初始化),又用大括号来指定数组大小(静态初始化),所以报错了。

3. 数组的使用

3.1 数组元素的下标访问

数组在内存中是一段连续的空间,空间的编号都是从0开始的,依次递增,该编号称为数组的下标,数组可以通过 下标访问其任意位置的元素。比如:

int[]array = new int[]{10, 20, 30, 40, 50}; 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]); // 也可以通过[]对数组中的元素进行修改array[0] = 100; System.out.println(array[0]);

tips:在Java中,方括号 [] 确实用于访问数组中的元素,但它并不被称为“下标访问运算符”或者索引运算符。这与其他支持运算符重载的语言有所不同。Java将 [] 视为数组访问的直接语法,仅用于数组,并没有定义为运算符或允许重载。

【注意事项】

数组是一段连续的内存空间,因此支持随机访问,即通过下标访问快速访问数组中任意位置的元素。 下标从0开始,介于[0, N)之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。(java.lang.ArrayIndexOutOfBoundsException异常代表数组越界访问)

3.2 计算数组长度

在数组中可以通过“ 数组对象.length ”来获取数组的长度。

例如:

输出:


为什么可以这样获得数组的长度?(简单了解)

我们注意到,我们用到了一个符号".",这个符号是成员访问运算符,用法就类似C语言中的结构体。Java中的对象就类似C语言的结构体(但会多了很多复杂的语法,比如对象中可以有方法)其实在Java中使用数组时,会自动导入“java.lang.reflect.Array”类。我们new的int[10]或String[4]等等,其实是new了(创建了)一个Array类型的对象。而Array类中有一个成员变量length,它记录着数组的长度。【可以简单得理解为C语言中的顺序表】

3.3 循环遍历数组的2种方式

\1. for语句

这是最简单的方法,例如:

int[] arr = new int[10];for(int i = 0; i < arr.length; i++){    System.out.println(arr[i]);}

\2. for - each语句

for-each 是 for 循环的另外一种使用方式。

语法格式:

for (元素类型 变量名 : 数组名) {

        // 循环体

}

“元素类型”是数组中元素的类型“变量名”是在每次迭代中用来引用当前元素的变量“数组名”是要遍历的数组

注意:迭代变量与数组名之间有一个冒号“ : ”

例如:

int[] arr = new int[10];for(int x: arr){    System.out.println(x);}
arr数组中的每个元素都是int型,所以创建的变量x是int类型的。第一次循环中,x等于arr[0];第二次循环中,x等于arr[1]……以此类推。

【两种遍历方式的区别】

循环条件控制:

for语句中的循环条件由程序员编写,可以完成更复杂的要求,但也容易出错。for-each语句不需要手动管理索引,使得代码的意图更加明确,减少了出错的可能性,但循环的条件不能手动改变。

元素访问方式:

for循环中通过访问数组下标会直接对数组元素进行操作。for-each循环通过迭代变量间接访问数组元素,即使发生错误操作也不会改变原数组的数据。

4. 数组是引用类型(内存空间的底层知识)

4.1 JVM的内存区域划分

JVM对所使用的内存按照功能的不同进行了划分,主要分为5的区域:

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

补充一点:字节码是与平台无关的中间代码表示形式。字节码是Java平台核心特性之一,它的存在使得Java程序具备了高度的可移植性和灵活性。

4.2 内存分配:基础类型的变量 和 引用类型的变量

基本数据类型创建的变量,该变量空间中直接存放的是其所对应的数值; 而引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址

例如:

public static void func() {    int a = 10;    int b = 20;    int[] arr = new int[]{1,2,3}; }
在上述代码中,a、b、arr,都是函数内部的局部变量,因此其空间都在func方法对应的栈帧中分配。(即它们的空间在栈区上)a、b是内置类型的变量,因此其空间中保存的就是给该变量初始化的值。array是数组类型的引用变量,其内部保存的内容可以简单理解成是数组在堆空间中的首地址,而数组的本体在堆区连续存放

引用变量并不直接存储对象本身,可以简单理解成存储的是对象在堆中空间的起始地址。通过该 地址,引用变量便可以去操作对象。有点类似C语言中的指针,但是Java中引用要比指针的操作更简单。

4.3 数组大小可变吗?

先说结论:数组的大小是固定的,一旦创建无法改变其大小

public static void main(String[] args) {        int arr[] = new int[10];        System.out.println(arr);        arr = new int[5];        System.out.println(arr);    }

可以发现,虽然我们用new的方式把数组arr的大小从10改变成了5,但前后两次arr存储的地址值不同了。

这说明数组的大小是无法改变的,每次使用new时都会重新创建一个新的数组。

4.4 “数组1 = 数组2”会发生什么?

arr1 = arr2,相当于把arr2所引用的地址交给了arr1,所以现在arr1和arr2都指向了同一个数组。arr1现在改变了引用的对象,如果arr1原来所引用的数组没有被其他引用变量引用,则系统会自动回收该堆上的空间。

【C语言中不支持“数组1 = 数组2”的语法】

例如:

public static void func() {        int[] array1 = new int[]{10,20,30};        int[] array2 = new int[]{1,2,3,4,5};        int[] t = array1;        array1 = array2;        array2 = t;                for(int x:array1){            System.out.print(x+" ");        }        System.out.println();        for (int x:array2){            System.out.print(x+" ");        }    }

运行结果:

可以发现array1和array2所引用的对象成功被交换。

【注意事项】

在对象交换的过程中,数组array1、数组array2和临时变量 t 都必须是同类型的。(刚刚的例子中它们都是int[]类型的变量)

4.5 空引用null

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

 int[] arr = null; System.out.println(arr[0]); // 执行结果Exception in thread "main" java.lang.NullPointerException at Test.main(Test.java:6)

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

注意事项:

1. 当引用类型的变量没有new一个对象的时候,必须要赋值null。

2. null不能写成NULL,这是因为:

NULL是“ (void*)0 ”的宏定义,在Java中既没有宏定义的语法,也没有指针的语法。

5. 数组 与 方法

5.1 数组作为方法参数

数组作为方法参数时,形参数组要与实参数组的数组类型一致

看一个例子:

public static void main(String[] args) {    int[] arr = {1, 2, 3};    func(arr);    System.out.println("arr[0] = " + arr[0]); } public static void func(int[] a) {    a[0] = 10;    System.out.println("a[0] = " + a[0]); } // 执行结果a[0] = 10arr[0] = 10

可以发现,在func方法内部修改数组的内容, 方法外部的数组内容也发生改变。

与C语言类似:数组作为参数传递的也是数组的地址

但不同的是:

C语言中的形参数组是一个“伪数组”,本质上是一个指针变量,所以形参的类型可以用指针类型来代替而Java中的形参数组是一个“真数组”,本质上是数组类型的引用变量,所以形参的类型不能用其他引用类型来代替。

将数组的地址传入到函数形参中,这样可以避免对整个数组的拷贝(数组可能比较长, 那么拷贝开销就会很大)。

5.2 数组作为方法返回类型

数组作为返回值,一般都是在方法内部new了一个数组对象,然后返回该对象的地址。

例如:

 public static int[] makeIntArray(int n){        int[] arr = new int[n];        return arr;    } public static void main(String[] args) {        int[] array = makeIntArray(10);    }

这种用法就类似C语言中,给顺序表中的数组用malloc函数或colloc函数创建数组一样。

6. Arrays的一些实用工具方法

Java为我们提供了一个专门处理数组的工具类Arrays,里面有一系列实用的方法。

【注意】我们本章使用的工具类是Arrays类,不是Array类:

数组类型:是Java语言的核心部分,没有特定的包路径。Array:位于 java.lang.reflect 包中,主要用于反射机制中的数组操作。Arrays:位于 java.util 包中,提供了更多实用的数组操作方法,适用于日常开发中的数组处理需求。

它们三者都是引用类型,前者是数组类型,后两者属于类类型;三者均无包含关系。

即使没有显式导入 Array 类,你仍然可以创建和操作数组,这是因为数组是Java语言的核心部分。不过Arrays需要显式导入“import  java.util.Arrays”

6.1 数组转字符串

toString方法可以把数组的信息转换为字符串。

参数是要被转化数组返回类型是字符串。原数组的信息不会被修改。

例如:

public static void main(String[] args) {        int[] arr = new int[]{1,2,3,4,5};        String str = Arrays.toString(arr);//把数组转换为字符串,由字符串str接收        //打印数组被转换后的结果        System.out.println(str);        //打印原数组        for(int x: arr){            System.out.print(x+" ");        }    }

输出结果:


模拟实现Arrays.toString

public static String myToString(int[] arr){        String str = "[";        for(int i = 0; i < arr.length; i++){            str += arr[i];            if(i != arr.length - 1){  //最后一个元素不要加逗号                str += ",";            }        }        str += "]";        return str;    }
public static void main(String[] args) {        int[] arr1 = new int[]{1,2,3,4,5};        String str = myToString(arr1);        System.out.println(str);    }

输出:

6.2 数组的拷贝

1. 两个参数的copyOf (数组名arr,拷贝长度N):

在方法中新创建一个长度为N的数组,把arr中的数据拷贝到该方法中的数组,最后返回该数组的地址。从序号0一直拷贝到下标N-1处。如果长度N大于arr的大小,则用默认值填充
public static void main(String[] args) {        int[] arr1 = new int[]{1,2,3,4,5};        int[] arr2 = Arrays.copyOf(arr1,7);                for(int x: arr2){            System.out.print(x+" ");        }    }

输出:


2. 三个参数的copyOfRange(数组名arr,整数1,整数2):

使用方式与上面的方法类似,区别是该方法是范围拷贝。拷贝范围是 [ 整数1,整数2 )。(左闭右开)拷贝的范围如果超出arr数组的右边,则用用默认值填充

例如:

public static void main(String[] args) {        int[] arr1 = new int[]{1,2,3,4,5};        int[] arr2 = Arrays.copyOfRange(arr1,3,7); //拷贝区间[3,7)                for(int x: arr2){            System.out.print(x+" ");        }    }

输出:

6.3 数组的排序

使用sort方法可以为数组排序

例如:

public class Blog {    public static void main(String[] args) {        int[] arr = {9, 5, 2, 7};        Arrays.sort(arr);        System.out.println(Arrays.toString(arr));    }}

6.4 二分查找指定元素

1. 两个参数的binarySearch (数组名arr,  要查找的元素key):

返回值 是key所在的序号如果找不到该元素,则会返回一个负数。

例如:

public static void main(String[] args) {        int[] arr = {0,1,2,3,4,5,6,7,8,9};        int pos = Arrays.binarySearch(arr,5);        System.out.println("要查找的元素位于序号"+pos);    }


二分查找方法的模拟

 public static int myBinarySearch(int[] arr, int key){        int left = 0;        int right = arr.length-1;        while(left <= right) {  //如果left不能等于right的话,那么当key在最右边时,会误以为找不到该元素            int mid = (left + right) / 2;            if(arr[mid] > key){                right = mid - 1;//此时要找的数据在mid左边,right变成mid-1,则区间[left,right]就位于mid左边了            }else if(arr[mid] < key){                left = mid + 1;//此时要找的数据在mid右边,left变成mid+1则区间[left,right]就位于mid右边了            }else{                return mid;            }        }        return -1;    }
public static void main(String[] args) {        int[] arr = {0,1,2,3,4,5,6,7,8,9};        int pos = myBinarySearch(arr,9);        System.out.println("要查找的元素位于序号"+pos);    }


2. 四个参数的binarySearch (数组名arr, 左边界k1, 右边界k2, 要查找的元素key):

效果与上一个方法类似,区别是在规定范围内查找,区间[ k1, k2 )(左闭右开)

例如:

public static void main(String[] args) {        int[] arr = {0,1,2,3,4,5,6,7,8,9};        int pos = Arrays.binarySearch(arr,6,9,9);//区间[6,9)        System.out.println("要查找的元素位于序号"+pos);        pos = Arrays.binarySearch(arr,6,10,9);//区间[6,10)        System.out.println("要查找的元素位于序号"+pos);    }

6.5 判断两个数组是否相等

使用equals(数组1,数组2)可以判断两个数组是否相等。

相等返回true,不相等返回false。

例如:

public static void main(String[] args) {        int[] arr = new int[]{1,2,3,4};        int[] arr1 = new int[]{1,2,3,4};        int[] arr2 = new int[]{1,2,3,3};        System.out.println(Arrays.equals(arr, arr1));        System.out.println(Arrays.equals(arr, arr2));    }

6.6 填充数组元素

1. 两个参数的 fill (数组名arr,填充值m):

用填充值m对数组arr中的所有元素进行赋值。该方法会直接操作数组arr。

例如:

 public static void main(String[] args) {        int[] arr = new int[]{1,2,3,4};        Arrays.fill(arr,5);        System.out.println(Arrays.toString(arr));    }


2. 四个参数的 fill (数组名arr,左边界k1,右边界k2,填充值m):

范围填充,区间是[ k1, k2 )

例如:

public static void main(String[] args) {        int[] arr = new int[]{1,2,3,4};        Arrays.fill(arr,1,3,6);        System.out.println(Arrays.toString(arr));    }


总的来说,数组作为Java编程中最基本且常见的数据结构之一,其重要性不言而喻。通过深入了解数组的特点、用法以及在实际应用中的优势,开发者可以更好地应用它来解决问题,提高代码的效率和可读性。


本期分享完毕,谢谢大家的支持Thanks♪(・ω・)ノ


点击全文阅读


本文链接:http://zhangshiyu.com/post/187688.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1