数组介绍
Java 语言中提供的数组是用来存储固定大小的同类型元素。
数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。
数组属引用类型,数组型数据是对象(object),数组中的每个元素相当于该对象的成员变量。
数组是多个相同类型数据的组合,实现对这些数据的统一管理。一个数组实际上就是一连串的变量,数组按照使用可以分为一维数组、二维数组、多维数组。
要使用Java的数组,必须经过两个步骤:
1)声明数组变量
2)创建数组:分配内存给这个数组
声明数组
首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:
元素类型[] 数组名; // 建议使用的方式
元素类型 数组名[]; // 效果相同,这种风格类似 C/C++ 语言
“数组名”是用来统一这组相同数据类型的元素的名称,建议使用有意义的名称为数组命名。
int[] score = null; // 推荐方式,null表示引用数据类型的默认值
// int socre[] = null; // 非推荐方式
为数组开辟空间
数组声明后实际上是在栈内存中保存了此数组的名称,接下来便是要在堆内存中配置数组所需的内存,其中“元素的个数”(数组的长度)是告诉编译器,所声明的数组要存放多少个元素,而“new”则是命令编译器根据括号里的长度开辟空间。
数组名 = new 元素类型[元素的个数];
我们为上面声明的socre数组开辟空间:
score = new int[3];
数组操作中,在栈内存中保存的永远是数组的名称,只开辟了栈内存空间数组是永远无法使用的,必须有指向的堆内存才可以使用,要想开辟新的堆内存则必须使用new关键字,之后只是将此堆内存的使用权交给了对应的栈内存空间,而且一个堆内存空间可以同时被多个栈内存空间指向,即:一个人可以有多个名字,人就相当于堆内存,名字就相当于栈内存。
数组的动态初始化
声明数组的同时分配内存:
元素类型 数组名[] = new 元素类型[元素的个数];
下面我们声明一个元素个数为10的整型数组score,同时开辟一块内存空间供其使用:
int[] score = new int[10];
在Java中,由于整数数据类型所占用的空间为4个bytes,而整型数组score可保存的元素有10个,所以上例中占用的内存共有4 * 10 = 40个字节。
数组是引用类型,它的元素相当于类的成员变量,因此数组一经分配空间,其中的每个元素也被按照成员变量同样的方式被隐式初始化。
public class ArrayTest {
public static void main(String[] args) {
// 对各元素进行初始化,但没有赋值。
String[] stringArray = new String[3]; // 各元素的值默认为String类型的初始值null
// 数组没有length()这个方法,有length的属性。
for (int i = 0; i < stringArray.length; i++) {
System.out.println(stringArray[i]); // null null null
}
}
}
数组中元素的表示方法
想要访问数组里的元素,可以利用索引来完成。Java的数组索引编号由0开始,以一个的score[10]的整形数组为例,score[0]代表第1个元素,score[1]代表第2个元素,score[9]为数组中第10个元素(也就是最后一个元素)。
程序中可以发现,对于数组的访问采用“数组名称[下标]”的方式,之前一共开辟了三个空间大小的数组,所以下标的取值是0~2,假设程序中取出的内容超过了这个下标,如“score[3]”,则程序运行的时候会出现数组下标越界的错误提示:java.lang.ArrayIndexOutOfBoundsException。
为数组中的元素赋值并进行输出:
public class Test {
public static void main(String[] args) {
int score[] = null; // 【1】声明数组,但未开辟堆内存
score = new int[3]; // 【2】为数组开辟堆内存空间,大小为3
for (int x = 0; x < score.length; x++) { // 数组的长度可以用“数组名.length”
score[x] = x * 2 + 1 ; // 为每一个元素赋值
} // 【3】开辟堆内存空间结束
for (int x = 0; x < 3; x++) { // 使用循环依次输出数组中的全部内容
System.out.println(“score[” + x + "] = " + score[x]);
}
}
}
我们对以上代码进行内存分析,【1】【2】【3】处分别对应下面这三张图:
数组的静态初始化
数组的内容分为动态初始化和静态初始化两种,前面所讲解的全部代码是采用先声明数组之后为数组中的每个内容赋值的方式完成的。那么也可以通过数组静态初始化在数组声明时就为数组元素分配空间并赋值。
元素类型 数组名 = {元素1,元素2…};
元素类型 数组名 = new 元素类型[]{元素1,元素2…};
数组的元素是通过索引访问的。数组索引从 0 开始,所以索引值从 0 到数组长度-1。
处理数组
数组的元素类型和数组的大小都是确定的,所以当处理数组元素时候,我们通常使用基本循环,尤其是for循环。JDK 1.5 引进了一种新的循环类型,被称为 foreach 循环或者加强型循环,它能在不使用下标的情况下遍历数组。
public class TestArray {
public static void main(String[] args) {
double[] arr = {1.5, 2.5, 3.5, 3.5, 5.5};
// 打印所有数组元素
for (double element: arr) {
System.out.println(element);
}
}
}
数组和前面讲到的八种基本数据类型一样也是一种数据类型,只不过它和后面要讲到的类、接口一样都是引用类型。既然是数据类型,那么就可以作为方法的参数类型(比如main方法的 string[] args)、方法的返回值类型使用,这个我们在方法篇再举例说明。
多维数组
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。我们以二维数组为例看一下多维数组的初始化:
二维数组的动态初始化:直接为每一维分配空间
元素类型 变量名 = new 元素类型[行数][列数];
声明整型数组score,同时为其开辟一块内存空间:
int[][] score = new int[4][3];
整型数据score可保存的元素有43 = 12个,而在Java中,int数据类型所占用的空间为4个字节,因此该整型数组占用的内存共为412 = 48个字节。
public class Test {
public static void main(String[] args) {
int score[][] = new int[4][3]; // 声明并实例化二维数组
score[0][1] = 30 ; // 为数组中的部分内容赋值
score[1][0] = 31 ; // 为数组中的部分内容赋值
score[2][2] = 32 ; // 为数组中的部分内容赋值
score[3][1] = 33 ; // 为数组中的部分内容赋值
score[1][1] = 30 ; // 为数组中的部分内容赋值
for (int i = 0; i < score.length; i++) { // 外层循环行
for(int j=0;j<score[i].length;j++){// 内层循环列
System.out.print(score[i][j] + “\t”);
}
System.out.println("") ; // 换行
}
}
}
执行结果如下:
0 30 0
31 30 0
0 0 32
0 33 0
二维数组的静态初始化:
元素类型 变量名 = {{元素A1,元素A2…}, {元素B1,元素B2}…};
例如
int score[][] = {{ 67, 61 }, { 78, 89, 83 }, { 99, 100, 98, 66, 95 }};
一般来讲,操作二维数组不应使用常数来控制维数。具体方法是array.length表示行数,array[row].length来表示row行的列数。这样当数组行数和列数不相等时,代码可以自动调整为正确的值。
public class Test {
public static void main(String[] args) {
// 静态初始化一个二维数组,每行的数组元素个数不一样
int[][] score = {{ 67, 61 }, { 78, 89, 83 }, { 99, 100, 98, 66, 95 }};
for (int i = 0; i < score.length; i++) { // 外层循环输出行
for (int j = 0; j < score[i].length; j++) {// 内存循环输出列
System.out.print(score[i][j] + “\t”);// 输出每一个元素
}
System.out.println(""); // 换行
}
}
}
执行结果如下:
67 61
78 89 83
99 100 98 66 95
操作数组的工具类:Arrays
java.util.Arrays类包含了用来操作数组(比如排序和搜索)的各种方法,它提供的所有这些方法都是静态的。
Arrays拥有一组static方法:
序号 方法和说明
1 public static int binarySearch(Object[] a, Object key)
用二分查找算法在给定数组中搜索给定值的对象(Byte,Int,double等)。数组在调用前必须排序好的。如果查找值包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。
2 public static boolean equals(long[] a, long[] a2)
如果两个指定的 long 型数组彼此相等,则返回 true。如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。换句话说,如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
3 public static void fill(int[] a, int val)
将指定的 int 值分配给指定 int 型数组指定范围中的每个元素。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
4 public static void sort(Object[] a)
对指定对象数组根据其元素的自然顺序进行升序排列。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
public class Test {
public static void main(String[] args) {
int array[] = { 2, 5, 8, 3, 1, 4, 9, 6, 7 }; // 声明一个整型数组
System.out.println("Object的toString:"array.toString()); //Object的toString()方法
Arrays.sort(array); // 数组排序
System.out.println("排序后的数组: " + Arrays.toString(array)) ;
int point = Arrays.binarySearch(array, 3) ; // 检索数据位置
System.out.println(“元素‘3’的位置在:” + point) ;
Arrays.fill(array, 6) ; // 填充数组
System.out.print(“数组填充:” + Arrays.toString(array)) ;
}
}
执行结果:
Object的toString:[I@7852e922
排序后的数组: [1, 2, 3, 4, 5, 6, 7, 8, 9]
元素‘3’的位置在:2
数组填充:[6, 6, 6, 6, 6, 6, 6, 6, 6]
System.arraycopy():array的复制。
使用System.arraycopy()方法进行数组拷贝,可以提高性能。
public class Test {
public static void main(String[] args) {
int[] a = { 3, 5, 6, 12, 45 }; // 声明一个整型数组a
int[] b = { 23, 12, 4, 53, 2}; // 声明一个整型数组b
System.arraycopy(a, 1, b, 2, 2);
System.out.println("数组a: " + Arrays.toString(a));
System.out.println("数组b: " + Arrays.toString(b));
}
}
执行结果:
数组a: [3, 5, 6, 12, 45]
数组b: [23, 12, 5, 6, 2]