数组
1. 基本概念
- 数据是编程语言常见的一种数据结构,可以存储多个数据,每个数组只能存储一种类型的数据,包括元素的赋值和取出数组的元素
- Java中,要求数组中的元素有相同的数据类型。因此,数组中的元素类型是唯一的。
- 数组一旦初始化完成,数组在内存中所占的空间将会被固定下来,因此,数据的长度将不可改变。
- 数组是引用类型,虽然int[] 中int是基本数据类型,但是int[]属于引用数据类型。
- 数组是引用数据类型,定义一个变量的时候,仅仅表示了定义了一个引用变量(也就是一个指针),这个变量未指向任何有效的内存,所以还没有内存空间来存储这些元素,这个数据还不能使用,只有初始化后才可以使用。
- 定义时,不能指定数组长度
- 初始化时,必须指定数组的长度
定义数据的两种方式
int[] arr
int arr []
Java中的数据,必须先进行初始化,然后才可以使用。
2. 初始化的方式
-
静态初始化
- 使用静态初始化,初始化数组时指定数组元素的初始值,不指定数组长度
//定义一个int数组类型的变量 //使用静态初始化,初始化数组时指定数组元素的初始值,不指定数组长度 int[] arr= {1,2,3,4}; String[] str = {"java","html","css","js"}
-
动态初始化
- arr = new int[length]
- length表示数组的长度
- 动态初始化时,程序员只需指定数组的长度,即为每个数组元素指定所需的内存空间,系统将负责为这些数组分配初始值。指定初始值时,系统按如下规则
- 整形:数组元素的初始值为0
- 浮点型,数组元素的初始值为0.0
- 字符型,数组元素的初始值为’\u0000’
- 布尔型,数组元素的初始值为false
- 引用类型(类,接口,数组),数组元素的初始值为null
-
注意:
- 不要同时使用动态初始化和静态初始化,不要在进行数组初始化时,即指定数组的长度,也为每个有数组元素分配初始值。
- 数组初始化完成后,就可以使用数组了,数组的赋值,访问数组元素值和获得数组长度等。
3. 使用数组
静态初始化
int[] arr = {1,2,3,4,5};
//获取数组中的某一个值
System.out.println(arr[4]);
//遍历数组
动态初始化
//定义一个长度为5的int类型数组
int[] arr = new int[5];
// 获取数组中索引为0的元素
System.out.println(arr[0]);
// 为数组元素赋值
arr[0] = 1;
System.out.println(arr[0]);
// 获取数组中元素为5的元素
System.out.println(arr[5]);
注意:
- Java语言数组索引是从0开始的,最后一个元素的索引是索引的长度-1
- 当访问数组的索引超过数组的长度范围时,会产生数组索引越界的错误
- java.lang.ArrayIndexOutOfBoundsException
- 这个报错信息是必须要记住的。
遍历数组
-
for循环
-
//例一: float[] arr = new float[5]; arr[0]=0.0f; arr[1]=1.0f; arr[2]=2.0f; arr[3]=3.0f; for (int a = 0; a < arr.length; a++) { System.out.println(arr[a]); } //例二: String[] arr = new String[10]; arr[0]="java"; arr[1]="c++"; arr[2]="c#"; arr[3]=".net"; for(int a =0;a<arr.length;a++) { System.out.println(arr[a]); }
-
-
foreach循环
-
float[] arr = new float[5]; arr[0]=0.0f; arr[1]=1.0f; arr[2]=2.0f; arr[3]=3.0f; for(float item:arr) { System.out.println(item); } String[] arr = {"java","c++","c#",".net"}; for(String item:arr) { System.out.println(item); }
-
相较于for循环
- 无需获得数组长度,也无需根据索引来获取数组。
- 无需循环条件,无需循环迭代语句。
-
注意:使用foreach循环时,不能将数据进行赋值操作。没有意义!
-
String[] arr = {"java","c++","c#",".net"}; for(String item:arr) { item = "java"; System.out.println(item); } System.out.println(Arrays.toString(arr));
-
发现并没有赋值成功。
- 原因:foreach的循环变量相当于一个临时变量,系统会把数组依次赋给这个临时变量,而这个临时变量并不是数组元素,它只是保存了数组的值。因此,如果希望改变数组元素的值,不能使用foreach循环。
-
-
练习题:
- 冒泡排序
- 选择排序
4. 深入数组
数组是一种引用数据类型,数组引用变量只是一个引用,数组元素和数组变量在内存里是分开存放的。
内存中的数组
-
数组引用变量只是一个引用,这个引用变量可以指向任何有效的内存,只有当该引用指向有效内存后,才可通过该数组变量来访问数组元素。l
-
引用变量是访问真是对象的根本方式。
-
[F@15db9742
-
数组的地址值,表示真实数组在内存中存储的地址。
-
-
-
栈和堆
- 栈:基本数据类型声明和值,引用类型的声明
- 堆:真实对象
- 为什么有堆栈之分?
- 当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量会逐渐分配到这些内存栈中,随着方法的执行结束,这个方法的内存栈也自然销毁。
- 局部变量放在栈内存,当方法结束后,这些局部变量随着栈的销毁而销毁。
- 引用类型的变量存储在堆之中,堆内存的对象不会随着方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用。只有当一个对象没有任何引用变量引用时,系统的垃圾回收器才会在合适的时候回收它。
-
如何让数组变为垃圾?null
-
只要类型相互兼容,就可以让一个数组变量指向另一个实际的数组,这种操作会让人产生数组长度可变的错觉。
-
//展示一: String[] arr = {"java","js"}; String[] brr = {"java","js","c#"}; System.out.println(Arrays.toString(arr)); System.out.println(Arrays.toString(brr)); arr=brr; System.out.println(Arrays.toString(arr)); //展示二: int[] a = {5,7,20}; int[] b = new int[4]; b=a; System.out.println(a.length);
-
-
当执行了b=a会后,堆内存中的第一个数组具有了两个引用;a变量和b变量都引用了第一个数组,第二个数组失去了引用,变成了垃圾,等垃圾回收机制来回收它。
-
int[] a = {5,7,10}; int[] b = new int[4]; b=a; a[0]=100; b[1]=500; System.out.println(Arrays.toString(a)); System.out.println(Arrays.toString(b));
-
基本类型数组初始化
基本类型数组,数组元素的值直接存储在对应的数组元素中,因此,初始化数组时,先为该数组分配内存空间,然后直接将数组元素的值存入对应数组元素中。
//定义一个int[]类型的数组变量
int[] iArr;
//动态初始化数组,数组长度为5
iArr=new int[5];
//采用循环方式为数组元素赋值
for(int i=0;i<iArr.length;i++) {
iArr[i]=i+10;
}
- 定义一个数组变量
- 动态初始化数组
- 为数组元素赋值
引用类型数组初始化
引用类型数组的数组元素是引用,每个数组元素里存储的还是引用,它指向另一块内存,这块内存里存储了有效的数据。
public class PersonArray {
public static void main(String[] args) {
//定义一个students数组变量,其类型是Person[]
Person[] students ;
//动态初始化
students= new Person[2];
//创建一个Person实例
Person zhang = new Person();
zhang.age=15;
zhang.height=158;
//创建一个Person实例
Person lee = new Person();
lee.age=16;
lee.height=161;
//为数组中元素赋值
students[0]=zhang;
students[1]=lee;
//执行实例对象的方法
lee.info();
students[1].info();
}
}
class Person {
public int age;
public double height;
public void info() {
System.out.println("我的年龄是:"+age+"我的身高是:"+height);
}
}
- 定义一个students数组变量,其类型是Person[]
- 动态初始化
- 创建Person实例
- 为数组中元素赋值
- 对比数组的赋值,我们将lee赋值给zhang
接上方代码
lee=zhang;
zhang.age=18;
System.out.println(zhang.age);
System.out.println(lee.age);
结果:二者输出一样。
原因:zhang和lee的引用指向同一个对象。
5. 多维数组
Java中提供了支持多维数组的语法,但是Java实际还是没有多维数组的。
原因:
- 数组类型是引用类型,因此数组变量是一个引用,这个引用指向真实的数组内存。如果将这个数组元素再次指向真实的数组内存,就看上去像一个多维数组。
- 数组类型: type[]。
- 定义一个数组,存储int类型的数据:int[]
- 定义一个数组,存储Person类型的数据:Person[]
- 定义一个数组,存储数组类型的数据:int[] []
public class Array {
public static void main(String[] args) {
//定义一个数组类型的数组,即二维数组
int[][] a;
//把a当成一个一维数组进行初始化,a是一个长度为4的数组
a= new int[4][];
//把a当做一个一位数组进行赋值。
for(int i=0;i<a.length;i++) {
System.out.println(a[i]);
}
//初始化第一个元素
a[0]= new int[2];
//为第一个数组中的第二个元素赋值
a[0][1]=6;
for(int i=0;i<a[0].length;i++) {
System.out.println(a[0][i]);
}
}
}
- 把a当成一个一维数组进行初始化,a是一个长度为4的数组把a当成一个一维数组进行初始化,a是一个长度为4的数组
- 为第一个数组中的第二个元素赋值
遍历二维数组
int[][] arr = new int[5][];
int[] childOne =new int[4];
childOne[0]=1;
childOne[1]=2;
int[] childTwo = new int[4];
childTwo[0]=1;
childTwo[1]=2;
int[] childThree = {1,2,3};
arr[0]=childOne;
arr[1]=childTwo;
arr[2]=childThree;
// System.out.println(Arrays.toString(childOne));
// System.out.println(Arrays.toString(childTwo));
// System.out.println(Arrays.toString(childThree));
// System.out.println(arr[0][0]);
// System.out.println(arr[2][2]);
for(int a =0;a<arr.length;a++) {
System.out.println(arr[a]);
for(int b=0;b<arr[a].length;b++) {
System.out.println(arr[a][b]);
}
}
问题:这样层层嵌套能否实现多维数组呢?
- 不能,Java是一个强类型语言,我们定义一个数组 int [ ] [ ] arr 表示我么创建了一个变量,这个变量的类型是数组类型,存储的数据也是数组类型,但是我们数组类型里面要求的是我们存储int类型的数据,因此,我们的二维数组里面就只能存储int类型的数据,不能再存放一个数组。
- 如何实现多层数组的存储呢?将数组的存储类型换成Object类型。