8. 数组
8. 数组
数组是编程语言中最常见的一种数据结构,可用于存储多个数据,每个数组元素存放一个数据,通常可通过数组元素的索引来访问数组元素,包括为数组元素赋值和取出数组元素的值。Java 语言的数组则具有其特有的特征,下面将详细介绍 Java 语言的数组。
理解数组
Java 的数组要求所有的数组元素具有相同的数据类型。因此,在一个数组中,数组元素的类型是唯一的,即一个数组里只能存储一种数据类型的数据,而不能存储多种数据类型的数据。
提示
因为 Java 语言是面向对象的语言,而类与类之间可以支持继承关系,这样可能产生一个数组里可以存放多种数据类型的假象。例如有一个水果数组,要求每个数组元素都是水果,实际上数组元素既可以是苹果,也可以是香蕉,但这个数组的数组元素的类型还是唯一的,只能是水果类型。
一旦数组的初始化完成,数组在内存中所占的空间将被固定下来,因此数组的长度将不可改变。即使把某个数组元素的数据清空,但它所占的空间依然被保留,依然属于该数组,数组的长度依然不变。
Java 的数组既可以存储基本类型的数据,也可以存储引用类型的数据,只要所有的数组元素具有相同的类型即可。
数组本身是一种引用类型。例如 int
是一个基本类型,但 int[]
(这是定义数组的一种方式)就是一种引用类型了。
定义数组
Java 语言支持两种语法格式来定义数组:
type[] arrayName;
type arrayName[];
对这两种语法格式而言,通常推荐使用第一种格式。因为第一种格式不仅具有更好的语意,而且具有更好的可读性。
相关信息
C# 就不再支持 type arrayName[]
这种语法,它只支持第一种定义数组的语法。越来越多的语言不再支持 type arrayName[]
这种数组定义语法。
数组是一种引用类型的变量,因此使用它定义一个变量时,仅仅表示定义了一个引用变量(也就是定义了一个指针),这个引用变量还未指向任何有效的内存,因此定义数组时不能指定数组的长度。
而且由于定义数组只是定义了一个引用变量,并未指向任何有效的内存空间,所以还没有内存空间来存储数组元素,因此这个数组也不能使用,只有对数组进行初始化后才可以使用。
提示
定义数组时不能指定数组的长度。
数组的初始化
Java 语言中数组必须先初始化,然后才可以使用。所谓初始化,就是为数组的数组元素分配内存空间,并为每个数组元素赋初始值。
提示
一旦为数组的每个数组元素分配了内存空间,每个内存空间里存储的内容就是该数组元素的值,即使这个内存空间存储的内容是空,这个空也是一个值(null)。
不管以哪种方式来初始化数组,只要为数组元素分配了内存空间,数组元素就具有了初始值。初始值的获得有两种形式:一种由系统自动分配,另一种由程序员指定。
数组的初始化有如下两种方式。
静态初始化:初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度。
动态初始化:初始化时程序员只指定数组长度,由系统为数组元素分配初始值。
静态初始化
静态初始化的语法格式如下:
type[] arrayName = new type[]{element1, element2, element3...};
前面的 type 就是数组元素的数据类型,此处的 type 必须与定义数组变量时所使用的 type 相同,也可以是定义数组时所指定的 type 的子类,并使用花括号把所有的数组元素括起来,多个数组元素之间以英文逗号 ,
隔开,定义初始化值的花括号紧跟[]之后。
值得指出的是,执行静态初始化时,显式指定的数组元素值的类型必须与 new 关键字后的 type 类型相同,或者是其子类的实例。
// 定义一个int数组类型的变量,变量名为intArr
int[] intArr;
// 使用静态初始化,初始化数组时只指定数组元素的初始值,不指定数组长度
intArr = new int[] {5, 6, 8, 20};
// 定义一个Object数组类型的变量,变量名为objArr
Object[] objArr;
// 使用静态初始化,初始化数组时数组元素的类型是
// 定义数组时数组元素类型的子类
objArr = new String[] {"Java", "李刚"};
Object[] objArr2;
// 使用静态初始化
objArr2 = new Object[] {"Java", "李刚"};
因为 Java 语言是面向对象的编程语言,能很好地支持子类和父类的继承关系:子类实例是一种特殊的父类实例。在上面程序中,String 类型是 Object 类型的子类,即字符串是一种特殊的 Object 实例。
除此之外,静态初始化还有如下简化的语法格式:
type[] arrayName = {element1, element2, element3...};
在这种语法格式中,直接使用花括号来定义一个数组,花括号把所有的数组元素括起来形成一个数组。
在实际开发过程中,可能更习惯将数组定义和数组初始化同时完成。
int[] arr = {1, 2, 3};
动态初始化
动态初始化只指定数组的长度,由系统为每个数组元素指定初始值。动态初始化的语法格式如下:
type[] arrayName = new type[length];
在上面语法中,需要指定一个 int 类型的 length 参数,这个参数指定了数组的长度,也就是可以容纳数组元素的个数。
与静态初始化相似的是,此处的 type 必须与定义数组时使用的 type 类型相同,或者是定义数组时使用的 type 类型的子类。
// 数组的定义和初始化同时完成,使用动态初始化语法
int[] prices = new int[5];
// 数组的定义和初始化同时完成,初始化数组时元素的类型是定义数组时元素类型的子类
Object[] books = new String[4];
执行动态初始化时,程序员只需指定数组的长度,即为每个数组元素指定所需的内存空间,系统将负责为这些数组元素分配初始值。
指定初始值时,系统按如下规则分配初始值。
- 数组元素的类型是基本类型中的整数类型(byte、short、int 和 long),则数组元素的值是 0。
- 数组元素的类型是基本类型中的浮点类型(float、double),则数组元素的值是 0.0。
- 数组元素的类型是基本类型中的字符类型(char),则数组元素的值是'\u0000'。
- 数组元素的类型是基本类型中的布尔类型(boolean),则数组元素的值是 false。
- 数组元素的类型是引用类型(类、接口和数组),则数组元素的值是 null。
注意
不要同时使用静态初始化和动态初始化,也就是说,不要在进行数组初始化时,既指定数组的长度,也为每个数组元素分配初始值。
使用数组
数组最常用的用法就是访问数组元素,包括对数组元素进行赋值和取出数组元素的值。
访问数组元素都是通过在数组引用变量后紧跟一个方括号 []
,方括号里是数组元素的索引值,这样就可以访问数组元素了。
访问到数组元素后,就可以把一个数组元素当成一个普通变量使用了,包括为该变量赋值和取出该变量的值,这个变量的类型就是定义数组时使用的类型。
Java 语言的数组索引是从 0 开始的,也就是说,第一个数组元素的索引值为 0,最后一个数组元素的索引值为数组长度减 1。
// 输出objArr数组的第二个元素,将输出字符串"李刚"
System.out.println(objArr[1]);
// 为objArr2的第一个数组元素赋值
objArr2[0] = "Spring";
如果访问数组元素时指定的索引值小于 0,或者大于等于数组的长度,编译程序不会出现任何错误,但运行时出现异常:java.lang.ArrayIndexOutOfBoundsException: N
(数组索引越界异常),异常信息后的 N 就是程序员试图访问的数组索引。
相关信息
为什么要记住这些异常信息?
编写一个程序,并不是单单指在电脑里敲出这些代码,还包括调试这个程序,使之可以正常运行。
没有任何人可以保证自己写的程序总是正确的,因此调试程序是写程序的重要组成部分,调试程序的工作量往往超过编写代码的工作量。
如何根据错误提示信息,准确定位错误位置,并排除程序错误是程序员的基本功。培养这些基本功需要记住常见的异常信息,以及对应的出错原因。
所有的数组都提供了一个 length 属性,通过这个属性可以访问到数组的长度,一旦获得了数组的长度,就可以通过循环来遍历该数组的每个数组元素。
// 使用循环输出prices数组的每个数组元素的值
for (int i = 0; i < prices.length; i++) {
System.out.println(prices[i]);
}
动态初始化的数组元素进行赋值,并通过循环方式输出每个数组元素。
// 对动态初始化后的数组元素进行赋值
books[0] = "疯狂Java讲义";
books[1] = "轻量级Java EE企业应用实战";
// 使用循环输出books数组的每个数组元素的值
for (int i = 0; i < books.length; i++) {
System.out.println(books[i]);
}
初始化一个数组后,相当于同时初始化了多个相同类型的变量,通过数组元素的索引就可以自由访问这些变量(实际上都是数组元素)。
使用数组元素与使用普通变量并没有什么不同,一样可以对数组元素进行赋值,或者取出数组元素的值。
foreach 循环
使用 foreach 循环遍历数组和集合元素时,无须获得数组和集合长度,无须根据索引来访问数组元素和集合元素,foreach 循环自动遍历数组和集合的每个元素。
foreach 循环的语法格式如下:
for(type variableName : array | collection) {
//variableName自动迭代访问每个元素..
}
在上面语法格式在中,type 是数组元素或集合元素的类型,variableName 是一个形参名,foreach 循环将自动将数组元素、集合元素依次赋给该变量。
public class ForEachTest {
public static void main(String[] args) {
String[] books = {"轻量级Java EE企业应用实战", "疯狂Java讲义", "疯狂Android讲义"};
// 使用foreach循环来遍历数组元素//其中book将会自动迭代每个数组元素
for (String book : books) {
System.out.println(book);
}
}
}
使用 foreach 循环遍历数组元素时无须获得数组长度,也无须根据索引来访问数组元素。foreach 循环和普通循环不同的是,它无须循环条件,无须循环迭代语句,这些部分都由系统来完成,foreach 循环自动迭代数组的每个元素,当每个元素都被迭代一次后,foreach 循环自动结束。
当使用 foreach 循环来迭代输出数组元素或集合元素时,通常不要对循环变量进行赋值,虽然这种赋值在语法上是允许的,但没有太大的实际意义,而且极易引起错误。
public class ForEachErrorTest {
public static void main(String[] args) {
String[] books = {"轻量级Java EE企业应用实战", "疯狂Java讲义", "疯狂Android讲义"};
// 使用foreach循环来遍历数组元素,其中book将会自动迭代每个数组元素
for (String book : books) {
book = "疯狂Ajax讲义";
System.out.println(book);
}
System.out.println(books[0]);
}
}
由于我们在 foreach 循环中对数组元素进行赋值,结果导致不能正确遍历数组元素,不能正确地取出每个数组元素的值。而且当再次访问第一个数组元素时,发现数组元素的值依然没有改变。
不难看出,当使用 foreach 来迭代访问数组元素时,foreach 中的循环变量相当于一个临时变量,系统会把数组元素依次赋给这个临时变量,而这个临时变量并不是数组元素,它只是保存了数组元素的值。
因此,如果希望改变数组元素的值,则不能使用这种 foreach 循环。
深入数组
数组是一种引用数据类型,数组引用变量只是一个引用,数组元素和数组变量在内存里是分开存放的。
内存中的数组
数组引用变量只是一个引用,这个引用变量可以指向任何有效的内存,只有当该引用指向有效内存后,才可通过该数组变量来访问数组元素。
与所有引用变量相同的是,引用变量是访问真实对象的根本方式。也就是说,如果我们希望在程序中访问数组对象本身,则只能通过这个数组的引用变量来访问它。
实际的数组对象被存储在堆(heap)内存中;如果引用该数组对象的数组引用变量是一个局部变量,那么它被存储在栈(stack)内存中。
如果需要访问如图所示堆内存中的数组元素,则程序中只能通过 p[index]
的形式实现。也就是说,数组引用变量是访问堆内存中数组元素的根本方式。
相关信息
为什么有栈内存和堆内存之分?
当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁。
因此,所有在方法中定义的局部变量都是放在栈内存中的;当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。
堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(在方法的参数传递时很常见),则这个对象依然不会被销毁。
只有当一个对象没有任何引用变量引用它时,系统的垃圾回收器才会在合适的时候回收它。
如果堆内存中数组不再有任何引用变量指向自己,则这个数组将成为垃圾,该数组所占的内存将会被系统的垃圾回收机制回收。
因此,为了让垃圾回收机制回收一个数组所占的内存空间,可以将该数组变量赋为 null,也就切断了数组引用变量和实际数组之间的引用关系,实际的数组也就成了垃圾。
只要类型相互兼容,就可以让一个数组变量指向另一个实际的数组,这种操作会让人产生数组的长度可变的错觉。
public class ArrayInRam {
public static void main(String[] args) {
// 定义并初始化数组,使用静态初始化
int[] a = {5, 7, 20};
// 定义并初始化数组,使用动态初始化
int[] b = new int[4];
// 输出b数组的长度
System.out.println("b数组的长度为:" + b.length);
// 循环输出a数组的元素
for (int i = 0, len = a.length; i < len; i++) {
System.out.println(a[i]);
}
// 循环输出b数组的元素
for (int i = 0, len = b.length; i < len; i++) {
System.out.println(b[i]);
}
// 因为a是int[]类型,b也是int[类型,所以可以将a的值赋给b。
// 也就是让b引用指向a引用指向的数组
b = a;
// 再次输出b数组的长度
System.out.println("b数组的长度为:" + b.length);
// 4
}
}
运行上面代码后,将可以看到先输出 b 数组的长度为 4,然后依次输出 a 数组和 b 数组的每个数组元素,接着会输出 b 数组的长度为 3。
看起来似乎数组的长度是可变的,但这只是一个假象。必须牢记:定义并初始化一个数组后,在内存中分配了两个空间,一个用于存放数组的引用变量,另一个用于存放数组本身。
当程序定义并初始化了 a、b 两个数组后,系统内存中实际上产生了 4 块内存区,其中栈内存中有两个引用变量:a 和 b;堆内存中也有两块内存区,分别用于存储 a 和 b 引用所指向的数组本身。
当执行上面的代码 b=a;
时,系统将会把 a 的值赋给 b,a 和 b 都是引用类型变量,存储的是地址。因此把 a 的值赋给 b 后,就是让 b 指向 a 所指向的地址。
当执行了 b=a;
之后,堆内存中的第一个数组具有了两个引用:a 变量和 b 变量都引用了第一个数组。此时第二个数组失去了引用,变成垃圾,只有等待垃圾回收机制来回收它——但它的长度依然不会改变,直到它彻底消失。
基本类型数组的初始化
对于基本类型数组而言,数组元素的值直接存储在对应的数组元素中,因此,初始化数组时,先为该数组分配内存空间,然后直接将数组元素的值存入对应数组元素中。
public class PrimitiveArrayTest {
public static void main(String[] args) {
// 定义一个int[]类型的数组变量
int[] iArr;
// 动态初始化数组,数组长度为5
iArr = new int[5];
// 采用循环方式为每个数组元素赋值
for (int i = 0; i < iArr.length; i++) {
iArr[i] = i + 10;
}
}
}
执行第一行代码 int[] iArr;
时,仅定义一个数组变量,此时内存中的存储示意图如图。
执行了 int[] iArr;
代码后,仅在栈内存中定义了一个空引用(就是 iArr 数组变量),这个引用并未指向任何有效的内存,当然无法指定数组的长度。
当执行 iArr=new int[5];
动态初始化后,系统将负责为该数组分配内存空间,并分配默认的初始值:所有数组元素都被赋值为 0。
此时 iArr
数组的每个数组元素的值都是 0,当循环为该数组的每个数组元素依次赋值后,此时每个数组元素的值都变成程序显式指定的值。
引用类型数组的初始化
引用类型数组的数组元素是引用,因此情况变得更加复杂。每个数组元素里存储的还是引用,它指向另一块内存,这块内存里存储了有效数据。
定义一个 Person
类:
class Person {
// 年龄
public int age;
// 身高
public double height;
// 定义一个info方法
public void info() {
System.out.println("我的年龄是:" + age + ",我的身高是:" + height);
}
}
定义一个 Person[]
数组,接着动态初始化这个 Person[]
数组,并为这个数组的每个数组元素指定值。
public class ReferenceArrayTest {
public static void main(String[] args) {
// 定义一个students数组变量,其类型是Person[]
Person[] students;
// 执行动态初始化
students = new Person[2];
// 创建一个Person实例,并将这个Person实例赋给zhang变量
Person zhang = new Person();
// 为zhang所引用的Person对象的age、height赋值
zhang.age = 15;
zhang.height = 158;
// 创建一个Person实例,并将这个Person实例赋给lee变量
Person lee = new Person();
// 为lee所引用的Person对象的age、height赋值
lee.age = 16;
lee.height = 161;
// 将zhang变量的值赋给第一个数组元素
students[0] = zhang;
// 将lee变量的值赋给第二个数组元素
students[1] = lee;
// 下面两行代码的结果完全一样,因为lee
// 和students[1]指向的是同一个Person实例
lee.info();
students[1].info();
}
}
执行 Person[] students;
代码时,这行代码仅仅在栈内存中定义了一个引用变量,也就是一个指针,这个指针并未指向任何有效的内存区。
栈内存中定义了一个 students
变量,它仅仅是一个引用,并未指向任何有效的内存。
直到执行初始化,本程序对 students
数组执行动态初始化,动态初始化由系统为数组元素分配默认的初始值:null,即每个数组元素的值都是 null。
students
数组的两个数组元素都是引用,而且这个引用并未指向任何有效的内存,因此每个数组元素的值都是 null。
这意味着依然不能直接使用 students
数组元素,因为每个数组元素都是 null,这相当于定义了两个连续的 Person
变量,但这个变量还未指向任何有效的内存区,所以这两个连续的 Person
变量(students
数组的数组元素)还不能使用。
接着的代码定义了 zhang
和 lee
两个 Person
实例,定义这两个实例实际上分配了 4 块内存,在栈内存中存储了 zhang
和 lee
两个引用变量,还在堆内存中存储了两个 Person
实例。
此时 students
数组的两个数组元素依然是 null,直到程序依次将 zhang
赋给 students
数组的第一个元素,把 lee
赋给 students
数组的第二个元素,students
数组的两个数组元素将会指向有效的内存区。
此时 zhang
和 students[0]
指向同一个内存区,而且它们都是引用类型变量,因此通过 zhang
和 students[0]
来访问 Person
实例的 Field
和方法的效果完全一样,不论修改 students[0]
所指向的 Person
实例的 Field
,还是修改 zhang
变量所指向的 Person
实例的 Field
,所修改的其实是同一个内存区,所以必然互相影响。
同理,lee
和 students[1]
也是引用同一个 Person
对象,也具有相同的效果。
多维数组
Java 语言里的数组类型是引用类型,因此,数组变量其实是一个引用,这个引用指向真实的数组内存。
数组元素的类型也可以是引用,如果数组元素的引用再次指向真实的数组内存,这种情形看上去很像多维数组。
但如果从数组底层的运行机制上来看,没有多维数组。
回到前面定义数组类型的语法:type[] arrName;
,这是典型的一维数组的定义语法,其中 type
是数组元素的类型。如果希望数组元素也是一个引用,而且是指向 int
数组的引用,则可以把 type
具体成 int[]
,那么上面定义数组的语法就是int[][] arrName
。
如果把 int
这个类型扩大到 Java 的所有类型(不包括数组类型),则出现了定义二维数组的语法:
type[][] arrName;
Java 语言采用上面的语法格式来定义二维数组,但它的实质还是一维数组,只是其数组元素也是引用,数组元素里保存的引用指向一维数组。
接着对这个“二维数组”执行初始化,同样可以把这个数组当成一维数组来初始化,把这个“二维数组”当成一个一维数组,其元素的类型是 type[]
类型,则可以采用如下语法进行初始化:
arrName = new type[length][];
上面的初始化语法相当于初始化了一个一维数组,这个一维数组的长度是 length。同样,因为这个一维数组的数组元素是引用类型(数组类型)的,所以系统为每个数组元素都分配初始值:null。
这个二维数组实际上完全可以当成一维数组使用:
使用
new type[length]
初始化一维数组后,相当于定义了 length 个type
类型的变量;使用
new type[length][]
初始化这个数组后,相当于定义了 length 个type[]
类型的变量;这些
type[]
类型的变量都是数组类型,因此必须再次初始化这些数组。
public class TwoDimensionTest {
public static void main(String[] args) {
// 定义一个二维数组
int[][] a;
// 把a当成一维数组进行初始化,初始化a是一个长度为4的数组
// a数组的数组元素又是引用类型
a = new int[4][];
// 把a数组当成一维数组,遍历a数组的每个数组元素
for (int i = 0, len = a.length; i < len; i++) {
System.out.println(a[i]);
}
// 初始化a数组的第一个元素
a[0] = new int[2];
// 访问a数组的第一个元素所指数组的第二个元素
a[0][1] = 6;
// a数组的第一个元素是一个一维数组,遍历这个一维数组
for (int i = 0, len = a[0].length; i < len; i++) {
System.out.println(a[0][i]);
}
}
}
把 a 这个二维数组当成一维数组处理,只是每个数组元素都是 null,所以我们看到输出结果都是 null。
程序的第一行 int[][] a;
,将在栈内存中定义一个引用变量,这个变量并未指向任何有效的内存空间,此时的堆内存中还未为这行代码分配任何存储区。
程序对 a 数组执行初始化:a = new int[4][];
,这行代码让 a 变量指向一块长度为 4 的数组内存,这个长度为 4 的数组里每个数组元素都是引用类型(数组类型),系统为这些数组元素分配默认的初始值:null。
虽然声明 a 是一个二维数组,但这里丝毫看不出它是一个二维数组的样子,完全是一维数组的样子。
这个一维数组的长度是 4,只是这 4 个数组元素都是引用类型,它们的默认值是 null。
所以程序中可以把 a 数组当成一维数组处理,依次遍历 a 数组的每个元素,将看到每个数组元素的值都是 null。
因为 a 数组的元素必须是 int[]
数组,所以接下来的程序对 a[0]
元素执行初始化,右边堆内存中的第一个数组元素指向一个有效的数组内存,指向一个长度为 2 的 int
数组。
因为程序采用动态初始化 a[0]
数组,因此系统将为 a[0]
所引用数组的每个元素分配默认的初始值:0,然后程序显式为 a[0]
数组的第二个元素赋值为 6。
初始化多维数组时,可以只指定最左边维的大小;当然,也可以一次指定每一维的大小。
// 同时初始化二维数组的两个数
int[][] b = new int[3][4];
上面代码将定义一个 b 数组变量,这个数组变量指向一个长度为 3 的数组,这个数组的每个数组元素又是一个数组类型,它们各指向对应的长度为 4 的 int[]
数组,每个数组元素的值为 0。
还可以使用静态初始化方式来初始化二维数组。使用静态初始化方式来初始化二维数组时,二维数组的每个数组元素都是一维数组,因此必须指定多个一维数组作为二维数组的初始化值。
// 使用静态初始化语法来初始化一个二维数组
String[][] str1 = new String[][] {new String[3], new String[] {"hello"}};
// 使用简化的静态初始化语法来初始化二维数组
String[][] str2 = {new String[3], new String[] {"hello"}};
操作数组的工具类
Java 提供的 Arrays 类里包含的一些 static 修饰的方法可以直接操作数组,这个 Arrays 类里包含了如下几个 static 修饰的方法(static 修饰的方法可以直接通过类名调用)。
int binarySearch(type[] a, type key)
:使用二分法查询 key 元素值在 a 数组中出现的索引;如果 a 数组不包含 key 元素值,则返回负数。调用该方法时要求数组中元素已经按升序排列,这样才能得到正确结果。int binarySearch(type[] a, int fromIndex, int toIndex, type key)
:这个方法与前一个方法类似,但它只搜索 a 数组中fromIndex
到toIndex
索引的元素。调用该方法时要求数组中元素已经按升序排列,这样才能得到正确结果。type[] copyOf(type[] original, int newLength)
:这个方法将会把 original 数组复制成一个新数组,其中 length 是新数组的长度。如果 length 小于 original 数组的长度,则新数组就是原数组的前面 length 个元素;如果 length 大于 original 数组的长度,则新数组的前面元素就是原数组的所有元素,后面补充 0(数值类型)、false(布尔类型)或者 null(引用类型)。type[] copyOfRange(type[] original, int from, int to)
:这个方法与前面方法相似,但这个方法只复制 original 数组的 from 索引到 to 索引的元素。boolean equals(type[] a, type[] a2)
:如果 a 数组和 a2 数组的长度相等,而且 a 数组和 a2 数组的数组元素也一一相同,该方法将返回 true。void fill(type[] a, type val)
:该方法将会把 a 数组的所有元素都赋值为val
。void fill(type[] a, int fromIndex, int toIndex, type val)
:该方法与前一个方法的作用相同,区别只是该方法仅仅将 a 数组的fromIndex
到toIndex
索引的数组元素赋值为val
。void sort(type[] a)
:该方法对 a 数组的数组元素进行排序。void sort(type[] a, int fromIndex, int toIndex)
:该方法与前一个方法相似,区别是该方法仅仅对fromIndex
到toIndex
索引的元素进行排序。String toString(type[] a)
:该方法将一个数组转换成一个字符串。该方法按顺序把多个数组元素连缀在一起,多个数组元素使用英文逗号,
和空格隔开。
public class ArraysTest {
public static void main(String[] args) {
// 定义一个a数组
int[] a = new int[] {3, 4, 5, 6};
// 定义一个a2数组
int[] a2 = new int[] {3, 4, 5, 6};
// a数组和a2数组的长度相等,每个元素依次相等,将输出true
System.out.println("a数组和a2数组是否相等:" + Arrays.equals(a, a2));
// 通过复制a数组,生成一个新的b数组
int[] b = Arrays.copyOf(a, 6);
System.out.println("a数组和b数组是否相等:" + Arrays.equals(a, b));
// 输出b数组的元素,将输出[3,4,5,6,0,0]
System.out.println("b数组的元素为:" + Arrays.toString(b));
// 将b数组的第3个元素(包括)到第5个元素(不包括)赋值为1
Arrays.fill(b, 2, 4, 1);
// 输出b数组的元素,将输出[3,4,1,1,0,0]
System.out.println("b数组的元素为︰" + Arrays.toString(b));
// 对b数组进行排序
Arrays.sort(b);
// 输出b数组的元素,将输出[0,0,1,1,3,4]
System.out.println("b数组的元素为:" + Arrays.toString(b));
}
}
提示
Arrays 类处于 java.util
包下,为了在程序中使用 Arrays
类,必须在程序中导入java.util.Arrays
。