数组的定义与使用
数组的基本定义
在程序开发之中考虑到一组变量的整体维护,专门提供有数组的概念。
数组的本质为:一组相关变量的集合,但是需要注意的是:在Java中将数组定义为的引用类型,所以数组的使用会要牵扯到内存的分配。
数组的动态初始化,初始化后,初始化之后数组每一个元素的保存内容为其对应数据类型的默认值
声明并初始化数组:
- 数据类型 数组名称[] = new 数据类型[长度];
- 数据类型[] 数组名称 = new 数据类型[长度];
数组的静态初始化:在数组定义的时候就为其设置好了里面的内容;
- 简化格式: 数据类型 数组名称[] = {数据1,数据2,数据3,…} ;
- 完整格式: 数据类型 数组名称[] = new 数据类型{数据1,数据⒉,数据3…};
当创建了一个数组之后就可以使用数组里面可以通过脚标进行每一个元素的访问,脚标从0开始定义,所以可以使用脚标范围:0 ~ 数组长度-1,同时要注意,使用时超出超出了数组脚标范围会出现“ArrayIndexOutOfBoundsException”(数组脚标越界)异常
public class ArrayDemo {
public static void main(String args[]) { //使用数组的动态初始化实现了数组的定义
int data[] = new int[3];
System.out.println(data[0]);//默认值 0
System.out.println(data[1]);//默认值 0
System.out.println(data[3]);//数组下标越界
}
}
for循环遍历
public class ArrayDemo {
public static void main(String args[]) {
//使用数组的动态初始化实现数组的定义
int data[] = new int[3];
data[0] = 10;//为数组赋值
data[1] = 20;
data[2] = 30;
for (int x = 0; x < data.length; x++) {
System.out.println(data[x]);
}
}
}
public class ArrayDemo {
public static void main(String args[]) {
//使用数组的静态初始化实现数组的定义
int data[] = new int[]{10, 20, 30};
for (int x = 0; x < data.length; x++) {
System.out.println(data[x]);
}
}
}
数组的引用传递
数组本身属于引用数据类型,所以引用传递应该还是按照传统方式那样:一个堆内存可以被多个栈内存所指向。
public class ArrayDemo {
public static void main(String args[]) {
//使用数组的静态初始化实现数组的定义
int data[] = new int[]{10, 20, 30};
data[0] = 99;
for (int x = 0; x < data.length; x++) {
System.out.println(data[x]);
}
}
}
由于数组属于引用类型,所以开辟堆内存空间之后才可以使用,如果现在使用了未开辟堆内存空间的数会出现“NullPointerException”异常。
public class ArrayDemo{
public static void main(String args[]){
int data[] = null;
System.out.println(data[0]);
}
}
必须提供有实例化对象才可以使用下标的形式进行数组的操作。
foreach输出
对于数组而言,一般都会使用for循环进行输出,但是在使用传统for循环输都会使用数组下标的方法进行数组元素的访问。
public class ArrayDemo {
public static void main(String args[]) {
int data[] = new int[]{1, 2, 3, 4, 5};
for (int i = 0; i < data.length; i++) {
System.out.println(data[i]);
}
}
}
而从JDK 1.5之后为了减轻下标对程序的影响(如果下标处理不当则会出现数组越界),所以参考了.NET中的设计,引入了一个增强型的 for循环( foreach),利用foreach的语法结构可以直接自动获取数组中的每一个元素,避免下标访问。
for(数据类型 变量:数组|集合){}
最大的特点在于可以自动将数组中的每一个元素的内容取出保存在变量里面,这样就可以直接通过变量访问数组内容,避免下标的方式来获取。防止数组下标越界
public class ArrayDemo {
public static void main(String args[]) {
int data[] = new int[]{1, 2, 3, 4, 5};
for (int temp : data) {
System.out.println(temp);
}
}
}
二维数组
传统的数组就好比一行数据,如果想要找到一个数据只需要确定一个下标即可
如果说现在你需要的是一个多行多列的结构(表),则就需要两个下标才能访问数据,行下标与列下标共同定义才能实现
二维数组的定义语法:
- 数组的动态初始化:
数据类型 数据名称[][] = new 数据类型[行个数][列个数];
- 数组的静态初始化:
数据类型 数据名称[][] = new 数据类型[][]{{数据,数据,数据…},{数据,数据,数据…},{数据,数据,数据…}};
public class ArrayDemo {
public static void main(String args[]) {
int data[][] = new int[][]{
{1, 2, 3}, {23, 34}, {5, 6, 7, 8}
};
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
System.out.print("data[" + i + "][" + j + "]=" + data[i][j]+"\t");
}
System.out.println();//换行
}
}
}
public class ArrayDemo {
public static void main(String args[]) {
int data[][] = new int[][]{
{1, 2, 3}, {23, 34}, {5, 6, 7, 8}
};
for (int temp[] : data) {
for (int num : temp) {
System.out.print(num + "\t");
}
System.out.println();
}
}
}
数组与方法
对于引用数据类型而言,主要的特点是可以与方法进行引用传递,而数组本身也属于引用类型,所以也可以用方法实现引用传递的操作。
public class ArrayDemo {
public static void main(String args[]) {
int data[] = new int[]{1, 2, 3, 4, 5};
printArray(data);//传递数组
}
//要求接受一个int型的数组
public static void printArray(int[] temp) {
for (int i = 0; i < temp.length; i++) {
System.out.println(temp[i]);
}
}
}
此时的内存关系:
public class ArrayDemo {
public static void main(String args[]) {
int data[] = initArray();//通过方法返回数组
printArray(data);//传递数组
}
//返回一个数组
public static int[] initArray() {
int arr[] = new int[]{1, 2, 3, 4, 5};
return arr;
}
//要求接受一个int型的数组
public static void printArray(int[] temp) {
for (int i = 0; i < temp.length; i++) {
System.out.println(temp[i]);
}
}
}
内存分析:
public class ArrayDemo {
public static void main(String args[]) {
int data[] = new int[]{1, 2, 3, 4, 5};
changeArray(data);//改变数组
printArray(data);//传递数组
}
//返回一个数组
public static void changeArray(int arr[]) {
for (int i = 0; i < arr.length; i++) {
arr[i] *= 2;//每个元素的内容乘2
}
}
//要求接受一个int型的数组
public static void printArray(int[] temp) {
for (int i = 0; i < temp.length; i++) {
System.out.println(temp[i]);
}
}
}
栗子:随意定义一个int数组,要求可以计算出这个数组元素的综合、最大值、最小值、平均值。
public class ArrayDemo {
public static void main(String args[]) {
int data[] = new int[]{1, 2, 3, 4, 5};
int sum = 0;
double avg = 0.0;
int max = data[0];//假设第一个是最大值
int min = data[0];//假设第一个是最小值
for (int i = 0; i < data.length; i++) {
sum += data[i];
if (max < data[i]) {
max = data[i];
}
if (min > data[i]) {
min = data[i];
}
}
avg = sum / data.length;
System.out.println("总和:" + sum);
System.out.println("平均值:" + avg);
System.out.println("最大值:" + max);
System.out.println("最小值:" + min);
}
}
但是不建议在主方法所在的类即主类中实现过于复杂的功能,在开发的过程中,主类就相当于一个客户端,而对于客户端的代码应该尽量简单一些,更多的应该是进行流程上的控制,而不是业务逻辑的实现,最好的做法是将这些操作去交给程序类去完成。
class ArrayUtil {//操作工具类
private int sum;//总和
private double avg;//平均值
private int max;//最大值
private int min;//保存最小值
public ArrayUtil(int data[]) {
this.max = data[0];//假设第一个是最大值
this.min = data[0];//假设第一个是最小值
for (int i = 0; i < data.length; i++) {
this.sum += data[i];
if (max < data[i]) {
this.max = data[i];
}
if (min > data[i]) {
this.min = data[i];
}
}
this.avg = sum / data.length;
}
private int getSum() {
return this.sum;
}
public double getAvg() {
return this.avg;
}
public int getMax() {
return this.max;
}
public int getMin() {
return this.min;
}
}
public class ArrayDemo {
public static void main(String args[]) {
int data[] = new int[]{1, 2, 3, 4, 5};
ArrayUtil util = new ArrayUtil(data);
System.out.println("总和:" + util.getSum());
System.out.println("平均值:" + util.getAvg());
System.out.println("最大值:" + util.getMax());
System.out.println("最小值:" + util.getMin());
}
}
数组操作案例:数组排序
数组排序指的是可以将一个砸落的数组按照顺序进行码放,但是对于数组排序总是通过一个基础的模型完成的。
public class ArrayDemo {
public static void main(String args[]) {
int[] data = new int[]{1, 2, 4, 44, 2, 5};
for (int i = 0; i < data.length - 1; i++) {
for (int j = 0; j < data.length - i - 1; j++) {
if (data[j] > data[j + 1]) {
int temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
}
}
}
printArray(data);
}
public static void printArray(int[] temp) {
for (int i = 0; i < temp.length; i++) {
System.out.print(temp[i] + "、");
}
}
}
以上的操作都是在主方法进行,不符合面向对象的设计结构。
class ArrayUtil {
public static void sort(int data[]) {
for (int i = 0; i < data.length - 1; i++) {
for (int j = 0; j < data.length - i - 1; j++) {
if (data[j] > data[j + 1]) {//交换数据
int temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
}
}
}
}
public static void printArray(int temp[]) {
for (int i = 0; i < temp.length; i++) {
System.out.print(temp[i] + "、");
}
System.out.println();//换行
}
}
public class ArrayDemo {
public static void main(String args[]) {
int data[] = new int[]{8, 9, 0, 2, 3, 4, 10, 7, 6, 5};
ArrayUtil.sort(data);//排序
ArrayUtil.printArray(data);
}
}
数组转置案例分析
数组的反转操作是指进行前后转置处理,即:首尾交换
数组内容:1,2,3,4,5,6,7,8,9
交换后的内容:9,8,7,6,5,4,3,2,1
- 方式1:定义一个新的数组而后按照逆序的方式保存(会产生无用的垃圾信息)
class ArrayUtil {
public static void printArray(int temp[]) {
for (int i = 0; i < temp.length; i++) {
System.out.print(temp[i] + "、");
}
System.out.println();//换行
}
}
public class ArrayDemo {
public static void main(String args[]) {
int data[] = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
int temp[] = new int[data.length];//第二个数组
int foot = temp.length - 1;//第二个数组的脚标
for (int i = 0; i < data.length; i++) {
temp[foot--] = data[i];
}
data = temp;
ArrayUtil.printArray(data);
}
}
- 方式2:在一个数组上进行转置
实现这种转置最需要确定的就是数组转换次数,次数的计算“数组的长度/2"
class ArrayUtil {
public static void printArray(int temp[]) {
for (int i = 0; i < temp.length; i++) {
System.out.print(temp[i] + "、");
}
System.out.println();//换行
}
}
public class ArrayDemo {
public static void main(String args[]) {
int data[] = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
int center = data.length / 2;//转换的次数
int head = 0;//操作脚标
int tail = data.length - 1;//操作的脚标
for (int i = 0; i < center; i++) {
int temp = data[head];
data[head] = data[tail];
data[tail] = temp;
head++;
tail--;
}
ArrayUtil.printArray(data);
}
}
class ArrayUtil {
public static void reverseArray(int[] data) {
int center = data.length / 2;//转换的次数
int head = 0;//操作脚标
int tail = data.length - 1;//操作的脚标
for (int i = 0; i < center; i++) {
int temp = data[head];
data[head] = data[tail];
data[tail] = temp;
head++;
tail--;
}
}
public static void printArray(int temp[]) {
for (int i = 0; i < temp.length; i++) {
System.out.print(temp[i] + "、");
}
System.out.println();//换行
}
}
public class ArrayDemo {
public static void main(String args[]) {
int data[] = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
ArrayUtil.reverseArray(data);
ArrayUtil.printArray(data);
}
}
数组相关类库
由于数组是一个重要的概念,所以在Java语言本身也提供了数组的相关支持处理。
数组排序:java.util.Arrays.sort(数组排序)
class ArrayUtil {
public static void printArray(int temp[]) {
for (int i = 0; i < temp.length; i++) {
System.out.print(temp[i] + "、");
}
System.out.println();//换行
}
}
public class ArrayDemo {
public static void main(String args[]) {
int data[] = new int[]{11, 2, 31, 4, 5, 36, 7, 8, 9};
java.util.Arrays.sort(data);//排序
ArrayUtil.printArray(data);
}
}
数组拷贝
System.arraycopy(源数组,源数组开始点,目标数组,目标数组开始点,拷贝长度)
栗子:实现数组拷贝.
现在假设有两个数组:.
数组一: 1、2、3、4、5、6、7、8、9;
数组二:11、22、33、44、55、66、77、88、99;
要求拷贝之后的数组二内容为: 11、22、33、6、7、8、77、88、99;
class ArrayUtil {
public static void printArray(int temp[]) {
for (int i = 0; i < temp.length; i++) {
System.out.print(temp[i] + "、");
}
System.out.println();//换行
}
}
public class ArrayDemo {
public static void main(String args[]) {
int dataA[] = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
int dataB[] = new int[]{11, 22, 33, 44, 55, 66, 77, 88, 99};
System.arraycopy(dataA, 5, dataB, 3, 3);//A数组的下标为5开始复制3个插入到数组B下标为3的位置
ArrayUtil.printArray(dataB);
}
}
这些操作的支持都是系统本身提供的,即:你都是可以在开发中使用的操作。
自己实现数组拷贝:
class ArrayUtil {
public static void printArray(int temp[]) {
for (int i = 0; i < temp.length; i++) {
System.out.print(temp[i] + "、");
}
System.out.println();//换行
}
public static void arraycopy(int[] src, int sindex, int[] desc, int dindex, int length) {
for (int i = 0; i < length; i++) {
desc[dindex++] = src[sindex++];
}
}
}
public class ArrayDemo {
public static void main(String args[]) {
int dataA[] = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
int dataB[] = new int[]{11, 22, 33, 44, 55, 66, 77, 88, 99};
ArrayUtil.arraycopy(dataA, 5, dataB, 3, 3);
ArrayUtil.printArray(dataB);
}
}
如果要是自己去定义这种拷贝或者排序的方法就需要考虑所有的数据类型。
方法可变参数
现在要求定义一个方法,这个方法可以实现任意多个整型数据的相加处理。
传统操作,使用数组:
class ArrayUtil {
public static int sum(int[] data) {
int sum = 0;
for (int temp : data) {
sum += temp;
}
return sum;
}
}
public class ArrayDemo {
public static void main(String args[]) {
System.out.println(ArrayUtil.sum(new int[]{1, 2, 3}));
}
}
虽然以上的程序可以实现任意多个数字的参数内容传递,但是与实际的要求并不符合,要求传递任意一个参数而不是一个数组。从JDK1.5开始为了方便开发者进行可变参数的定义,对于方法的参数提供有可变参数的支持
class ArrayUtil {
public static int sum(int… data) {//变种数组
int sum = 0;
for (int temp : data) {
sum += temp;
}
return sum;
}
}
public class ArrayDemo {
public static void main(String[] args) {
System.out.println(ArrayUtil.sum(1, 2, 3));//6
System.out.println(ArrayUtil.sum(new int[]{1, 2, 3}));//6
}
}
可变参数的最大作用在于,在以后进行一些程序类设计或者开发者调用的时候,利用此种形式就可以避免数组的传递操作,可变参数的本质就是数组
对象数组
在之前所接触到的都是基本数据类型定义的数组,但是在Java程序本身各种数据类型都可以成为数组类型,所以类也可以成为数组类型,我们称为对象数组。
- 动态初始化:
类对象数组 名称=new 类[长度]; //每一个元素的内容都是null;
- 静态初始化:
类对象数组 名称=new 类{实例化对象,实例化对象,…};
//动态初始化
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getInfo() {
return "姓名:" + this.name + "、年龄:" + this.age;
}
//getter、setter略
}
public class ArrayDemo {
public static void main(String args[]) {
Person per[] = new Person[3];
per[0] = new Person("张三", 20);
per[1] = new Person("李四", 20);
per[2] = new Person("王五", 20);
for (int i = 0; i < per.length; i++) {
System.out.println(per[i].getInfo());
}
}
}
//静态初始化
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getInfo() {
return "姓名:" + this.name + "、年龄:" + this.age;
}
//getter、setter略
}
public class ArrayDemo {
public static void main(String args[]) {
Person[] per = new Person[]
{
new Person("张三", 20),
new Person("李四", 20),
new Person("王五", 20)
};
for (int i = 0; i < per.length; i++) {
System.out.println(per[i].getInfo());
}
}
}
数组的缺点:长度固定;
数组的优点:数据线性保存,根据索引访问,速度较快(时间的复杂度1)