OOP2:数组
总结了定义数组的方式、访问数组元素和数组长度的方法、用 for 循环遍历数组的方式、数组作为方法参数和方法返回值的用法、数组作为成员变量如何构造、如何复制数组、如何对数组进行排序和查找、Arrays 类和二维数组的相关知识点。
一、数组的定义
使用数组一般需要两个步骤:①声明数组:向编译器声明数组名称和元素的数据类型。②创建数组:为数组元素分配存储空间。
1. 声明数组
java 语言的数组是一种引用数据类型,即数组是对象,而数组名就是对象名(或引用名,即对象的引用,是一个引用变量)。数组的声明实际上是声明一个引用变量,形如:
java
元素类型 []数组名;
元素类型 数组名[];
元素类型[] 数组名;//三种形式等价,推荐用第一种
下面代码声明了两个数组:
java
double []marks;
Account []accounts;
数组元素类型可以是基本数据类型 (如 int 或 double 类型),也可以是引用数据类型 (如 String 或 Account 类型)。如果数组元素为引用类型,则该数组称为对象数组 。上面的 accounts 就是对象数组。声明数组不能指定数组元素的个数。
2. 创建数组
声明数组仅仅声明一个数组对象的引用,而创建数组才会为数组的每个元素分配存储空间。创建数组用 new 运算符,形如:
java
数组名 = new 元素类型[size];//size 指定大小
下面代码创建了 marks 数组和 accounts 数组:
java
marks = new double[5];
accounts = new Account[3];
Java 数组可以动态创建,也就是它的大小可以在运行时指定,可以以变量作为数组的大小。
数组的声明与创建可以写在一个语句中,如下所示:
java
double[] marks = new double[5];
Account[] accounts = new Account[3];
创建数组时会为每个元素指定默认值,对于引用类型数组(对象数组),它的每个元素初值为 null ,因此,还需要创建数组元素对象。
java
accounts[0] = new Account(101, "Fish Yu", 3000.0);
accounts[1] = new Account(102, "Tina Wang", 5000.0);
accounts[2] = new Account(103, "Tear", 8000.0);
也可以用下面这种形式完成对象数组的声明-创建-初始化:
java
Account[] accounts = new Account[]
{
new Account(101, "Fish Yu", 3000.0);
new Account(102, "Tina Wang", 5000.0);
new Account(103, "Tear", 8000.0);
}
3. 数组初始化器
初始化器是在一对花括号中给出数组的每个元素值,这种方式称为静态初始化。用这种方法创建数组不能指定大小,系统会根据元素个数确定数组大小。如下所示:
java
double[] marks = {11, 45, 14, 1919, 810};
Account[] accounts = {new Account(101, "Fish Yu", 3000.0), new Account(102, "Tina Wang", 5000.0), new Account(103, "Tear", 8000.0)};
可以用 var 类型推断符声明数组,减少代码冗余:
java
var marks = {11, 45, 14, 1919, 810};
var accounts = {new Account(101, "Fish Yu", 3000.0), new Account(102, "Tina Wang", 5000.0), new Account(103, "Tear", 8000.0)};
"声明-创建-初始化"的多种形式:
下面两种"声明-创建"写法均合法:
java
double[] marks = new double[5];
java
var marks = new double[5];
下面三种"声明-创建-初始化"写法均合法:
java
double[] marks = {11, 45, 14, 1919, 810};
java
double[] marks = new double[]{11, 45, 14, 1919, 810};
java
var marks = new double[]{11, 45, 14, 1919, 810};
下面的写法不合法,因为没有充分的依据进行类型推断:
java
var marks = {11, 45, 14, 1919, 810};
二、访问数组
1. 访问数组元素
数组元素的使用方式如下:
数组名[下标]
数组元素下标从0开始,到数组长度减1。数组一经创建,大小不能改变。Java 运行时会对数组元素的范围进行越界检查,若数组元素的下标超出范围,会抛出 ArrayIndexOutOfBoundsException 运行时异常。
2. 访问数组长度
数组作为对象提供一个 length 成员变量,它的值是数组元素的个数,访问方法如下:
数组名.length
3. 增强的 for 循环
增强的 for 循环能按顺序访问数组元素,可以用来迭代数组和集合对象的每个元素。因此这种循环也称为 for each 循环。形如:
java
for(type identifier : expression)
{
//循环体
}
type 是元素类型,expression 必须是一个数组或集合对象,identifier 是元素本身,后面不需要再加中括号和下标。
下面代码求数组 marks 中各元素的和:
java
double sum = 0;
for(var score : marks)
{
sum = sum + score;
}
System.out.println("总成绩 = " + sum);
注意:增强的 for 循环只能按顺序访问数组元素,并且只能使用元素而不能对元素进行修改,不能获取当前元素的索引(下标)。
三、数组的应用
1. 数组元素的复制
数组元素的复制,是将一个数组中的所有元素复制到另一个同类型的数组中,得到的是两个彼此独立但又一模一样的数组。设有一个数组 source ,其中有4个元素,现在定义一个数组 target ,与原来数组的类型相同,元素个数相同。
区分:元素复制 和引用赋值。
类似于对象名是对象的引用,数组名也是数组的引用。如果直接用赋值号 连接两个数组名,实际上是使前者指向后者所引用的数组对象,即令两个数组引用指向同一个数组对象。如下所示:
java
int[] source = {10, 30, 20, 40};
int[] target = source;
上述代码无法将数组 source 中的每个元素复制到 target 数组中。
数组元素复制的方法:
方法一:使用循环结构,将数组元素一个一个复制到目标数组中。
java
int[] source = {10, 30, 20, 40};
int[] target = new int[source.length];
for(var i = 0; i < source.length; i++)
target[i] = source[i];
方法二:使用 System 类的 arraycopy() 方法,格式如下:
java
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
src 为原数组,srcPos 为原数组的起始下标,dest 为目标数组,destPos 为目标数组的下标,length 为复制的数组元素个数。下面代码实现将 source 中每个元素复制到数组 target 中:
java
int[] source = {10, 30, 20, 40};
int[] target = new int[source.length];
System.arraycopy(source, 0, target, 0, 4);
使用 arraycopy() 方法也可以将源数组的一部分复制到目标数组中。注意,如果目标数组不足以容纳原数组元素,会抛出异常。
2. 数组冒泡排序
算法与写法和 C 语言中大同小异,直接展示代码:
java
public static void bubbleSort(int[] array)
{
for(int i = array.length - 1; i > 0; i--)
{
boolean flag = true;
for(int j = 0; j < i; j++)
{
if(array[j] > array[j + 1])
{
int t = array[j];
array[j] = array[j + 1];
array[j + 1] = t;
flag = false;
}
}
if(flag) break;
}
}
将该静态方法和主方法放在同一个类,然后即可在主方法中调用该方法。
3. java.util.Arrays 类
java.util.Arrays 类定义了若干静态方法对数组操作,下面介绍实用的几个。
-
public static int binarySearch(int[] a, int key)对于一个已升序排序的数组,通过二分查找快速定位 key 值位置。找到则返回下标,找不到则返回(插入点-1)。插入点为指定的值在数组中应该插入的位置。
-
public static void sort(int[] a)对数组 a 按升序排序。
-
public static void sort(int[] a, int fromIndex, int toIndex)对数组 a 中从起始下标 fromIndex (包含)到终止下标 toIndex (不包含)之间的元素排序。
-
public static void fill(int[] a, int val)用指定的 val 值填充数组 a 中的每个元素。
-
public static double[] copyOf(double[] original, int newLength)对原数组 original 进行复制,返回值为接收复制的新数组。新数组的长度可用参数 newLength 定义,若不想改变原数组长度,可填成 original.length。
4. 数组与方法
(1)数组作方法参数
数组对象可以作用参数传递给方法,下面代码定义了一个求数组元素和的方法:
java
public static double sumArray(double array[])
{
var sum = 0;
for(var i = 0; i < array.length; i++)
sum += array[i];
return sum;
}
注意:由于数组是对象 ,因此将其传递给方法是按引用传递。如果在方法体中修改了数组元素的值,则该修改反映到返回的数组对象。
(2)方法返回数组
一个方法可以返回一个数组对象,下面的方法返回参数数组的元素反转后的一个数组:
java
public static int[] reverse(int[] list)
{
var result = new int[list.length];
for(int i = 0, j = result.length - 1; i < list.length; i++, j--)
result[j] = list[i];
return result;
}
有了上述方法,可以使用如下语句实现数组的反转:
java
int[] list = {6, 7, 8, 9, 10};
int[] list2 = reverse(list);
(3)可变参数方法
Java 允许定义方法(包括构造方法)带可变数量的参数,这种方法称为可变参数方法 。具体做法是在方法参数列表的最后一个参数的类型名之后、参数名之前使用省略号,如下所示:
java
public static int average(int ... array)
{
//方法体
}
带可变参数的方法中也可以有一般的参数,但可变参数必须是方法的最后一个参数。如下所示:
java
public static int average(String name, int ... array)
{
//方法体
}
这里,参数 values 被声明为一个 double 型值的序列,其中参数的类型可以是引用类型 。对可变参数的方法,调用时可以为其传递任意数量的指定类型的实际参数。
在方法体中,编译器将为可变参数创建一个数组,并将传递来的实际参数值作为数组元素的值,相当于为方法传递一个指定类型、长度不定的数组。
下面程序使用可变参数方法求和,加数个数不定。
程序 2-1 VarargsDemo.java
java
public class VarargsDemo
{
//可变参数方法,可变参数类型定义成:类型...argName,它必须是方法的最后一个参数
public static int getSum(int ... array)
{
var sum = 0;
for(var i : array)
{
sum += i;
}
return sum;
}
public static void main(String[] args)
{
int result = getSum(2, 3, 4);
System.out.println("2+3+4 = " + result);
result = getSum(3, 5, 8, 9, 10);
System.out.println("3+5+8+9+10 = " + result);
int[] a = {1,2,3,4};
result = getSum(a);
System.out.println("a数组元素之和为 " + result);
}
}
运行结果如下:
该程序调用了 getSum()方法并为其传递若干 int 型数,也可以传递一个 int 型数组。
5. 数组作成员变量
(1)设计构造方法
现有类 MyArray,带有私有成员数组 data。在类中进行代码设计。
java
public class MyArray
{
private int []data;
//构造方法
}
- 浅拷贝(直接引用)
java
public MyArray(int[] inputArray)
{
this.data = inputArray
}
通过传递的参数 inputArray 为 data 赋值,但由于是引用赋值,若 inputArray 改变,则对象内的 data 也会随之改变。
- 深拷贝(创建副本)
java
public MyArray(int[] inputArray)
{
this.data = Arrays.copyOf(intputArray, inputArray.length);
}
上述代码为传来的参数 inputArray 创建了副本,再赋值给 data ,因此 data 不会被外部篡改。这种写法无疑安全性更高,故下面主要使用这种写法。
(2)getter / setter
java
//getter 返回副本
public int[] getData()
{
return Arrays.copyOf(data, data.length);
}
//setter 接收副本
public void setData(int[] newData)
{
this.data = Arrays.copyOf(newData, newData.length);
}
(3)使用对象
在主方法中创建对象,并获取/修改对象成员的值。
- 创建对象
java
//写法一:先另定义数组,再将其作为参数写入构造方法
int[] source = {1, 2, 3};
MyArray array1 = new MyArray(source)
//写法二:直接在构造方法参数列表中定义数组
MyArray array2 = new MyArray(new int[]{1, 2, 3});
- 获取/修改对象
java
//获取数组
int[] got = array1.getData();
//修改数组
array1.setData(new int[]{4, 5, 6});
四、二维数组
Java 语言中数组元素还可以是一个数组,这样的数组称为数组的数组 或二维数组。
1. 二维数组的定义
二维数组的使用也分声明、创建两个步骤。
(1)二维数组的声明
二维数组有下面3种等价的声明格式:
java
元素类型 [][]数组名;//推荐使用第一种格式
元素类型 数组名[][];
元素类型 []数组名[];
元素类型可以是基本类型 ,也可以是引用类型,示例:
java
int [][]matrix;
String [][]cities;
(2)二维数组的创建
创建二维数组即为每个元素分配存储空间。系统先为高维分配引用空间,然后顺次为低维分配空间。
- 写法一:直接为每一维分配空间:
java
var matrix = new int[2][3];
适用于数组的低维具有相同个数的数组元素。
二维数组是数组的数组,即数组元素也是一个数组。二维数组 matrix 有两个元素,matrix[0] 和 matrix[1] ,它们又都是数组,各有3个元素。matrix、matrix[0]、matrix[1]都是对象。每个元素在创建时会被指定默认值。
- 写法二:先为第一维分配空间,再为第二维分配空间(反之不可):
java
var matrix = new int[2][];
matrix[0] = new int[3];
matrix[1] = new int[4];
适用于低维数组元素个数不同的情况。
第一维的每个元素(matrix[0]、matrix[1])可以指定不同的大小。
对于引用类型的数组,除了为数组分配空间外,还要为每个数组元素分配空间:
java
cities[0][0] = new String("成都");
cities[0][1] = new String("绵阳");
cities[0][2] = new String("雅安");
cities[1][0] = new String("沈阳");
cities[1][1] = new String("锦州");
(3)数组初始化器
二维数组可使用初始化器在声明数组的同时为数组元素初始化,如下所示:
java
int[][] matrix = {{15, 56, 20, -2}, {10, 80, -9, 31}, {76, -3, 99, 21}};
2. 数组元素的使用
用下面的形式访问二维数组的元素:
数组名[下标1][下标2]
下标1和下标2可以是整型常数或表达式。每一维的下标是从0到该维长度减1.
- 访问数组的长度:
若有数组 matrix[3][4] ,则说明 matrix 数组是一个3行4列的数组。当我们讨论 matrix 的长度时,我们其实指的是它第一维的长度。多维数组每一维都有一个 length 成员表示数组的长度。matrix.length 的值是3,matrix[0].length 的值是4.