目录
1. 数组的基本概念
1.1 为什么要使用数组
1.2 什么是数组
1.3 数组的创建及初始化
1.3.1 数组的创建
1.3.2 数组的初始化
1.4 数组的使用
1.4.2 遍历数组
2. 数组是引用类型
2.1 初始JVM的内存分布
2.2 基本类型变量与引用类型变量的区别
2.3 再谈引用变量
2.4 认识 null
3. 数组的应用场景
3.1 保存数据
3.2 作为函数的参数
3.3 作为函数的返回值
4. 数组练习
4.1 数组转字符串
4.2 数组拷贝
4.3 求数组中元素的平均值
4.4 查找数组中指定元素(顺序查找)
4.5 查找数组中指定元素(二分查找)
4.6 数组排序(冒泡排序)
4.7 数组逆序
5. 二维数组
1. 数组的基本概念
1.1 为什么要使用数组
public class TestStudent{
public static void main(String[] args){
int score1 = 70;
int score2 = 80;
int score3 = 85;
int score4 = 60;
int score5 = 90;
System.out.println(score1);
System.out.println(score2);
System.out.println(score3);
System.out.println(score4);
System.out.println(score5);
}
}
上述代码没有任何问题, 但不好的是 :如果有 20 名同学成绩呢,需要创建 20 个变量吗?有 100 个学生的成绩那不得要创建100 个变量。仔细观察这些学生成绩发现: 所有成绩的类型都是相同的 ,那 Java 中存在可以存储相同类型多个数据的类型吗?这就是本节要将的数组。
1.2 什么是数组
1.3 数组的创建及初始化
1.3.1 数组的创建
T[] 数组名 = new T[N];
T:表示数组中存放元素的类型T[]:表示数组的类型N:表示数组的长度
int[] array1 = new int[10]; // 创建一个可以容纳10个int类型元素的数组
double[] array2 = new double[5]; // 创建一个可以容纳5个double类型元素的数组
String[] array3 = new double[3]; // 创建一个可以容纳3个字符串元素的数组
1.3.2 数组的初始化
int[] array = new int[10];
语法格式: T[] 数组名称 = {data1, data2, data3, ..., datan};
int[] array1 = new int[]{0,1,2,3,4,5,6,7,8,9};
double[] array2 = new double[]{1.0, 2.0, 3.0, 4.0, 5.0};
String[] array3 = new String[]{"hell", "Java", "!!!"};
【注意事项】静态初始化虽然没有指定数组的长度,编译器在编译时会根据{}中元素个数来确定数组的长度。静态初始化时, {}中数据类型必须与[]前数据类型一致。静态初始化可以简写,省去后面的new T[]。
// 注意:虽然省去了new T[], 但是编译器编译代码时还是会还原
int[] array1 = {0,1,2,3,4,5,6,7,8,9};
double[] array2 = {1.0, 2.0, 3.0, 4.0, 5.0};
String[] array3 = {"hell", "Java", "!!!"};
/*该种定义方式不太友好,容易造成数组的类型就是 int 的误解[] 如果在类型之后,就表示数组类型,因此 int[] 结合在一块写意思更清晰*/int arr [] = { 1 , 2 , 3 };
int[] array1;
array1 = new int[10];
int[] array2;
array2 = new int[]{10, 20, 30};
// 注意省略格式不可以拆分, 否则编译失败
// int[] array3;
// array3 = {1, 2, 3};
1.4 数组的使用
int[]array = new int[]{10, 20, 30, 40, 50};
System.out.println(array[0]);
System.out.println(array[1]);
System.out.println(array[2]);
System.out.println(array[3]);
System.out.println(array[4]);
// 也可以通过[]对数组中的元素进行修改
array[0] = 100;
System.out.println(array[0]);
【 注意事项 】1. 数组是一段连续的内存空间,因此 支持随机访问,即通过下标访问快速访问数组中任意位置的元素。2. 下标从 0 开始,介于 [0, N )之间不包含 N , N 为元素个数,不能越界,否则会报出下标越界异常。
int[] array = {1, 2, 3};
System.out.println(array[3]); // 数组中只有3个元素,下标一次为:0 1 2,array[3]下标越界
// 执行结果
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100
at Test.main(Test.java:4)
1.4.2 遍历数组
int[]array = new int[]{10, 20, 30, 40, 50};
System.out.println(array[0]);
System.out.println(array[1]);
System.out.println(array[2]);
System.out.println(array[3]);
System.out.println(array[4]);
上述代码可以起到对数组中元素遍历的目的,但问题是:1. 如果数组中增加了一个元素,就需要增加一条打印语句2. 如果输入中有100个元素,就需要写100个打印语句3. 如果现在要把打印修改为给数组中每个元素加1,修改起来非常麻烦。
int[]array = new int[]{10, 20, 30, 40, 50};
for(int i = 0; i < 5; i++){
System.out.println(array[i]);
}
int[]array = new int[]{10, 20, 30, 40, 50};
for(int i = 0; i < array.length; i++){
System.out.println(array[i]);
}
int[] array = {1, 2, 3};
for (int x : array) {
System.out.println(x);
}
2. 数组是引用类型
2.1 初始JVM的内存分布
1.程序计数器 (PC Register): 只是一个很小的空间 , 保存下一条执行的指令的地址2.虚拟机栈 (JVM Stack): 与方法调用相关的一些信息, 每个方法在执行时,都会先创建一个栈帧 ,栈帧中包含有:局部变量表 、 操作数栈 、 动态链接 、 返回地址 以及其他的一些信息,保存的都是与方法执行时相关的一些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了 。3.本地方法栈 (Native Method Stack): 本地方法栈与虚拟机栈的作用类似 . 只不过 保存的内容是 Native 方法的局 部变量 . 在有些版本的 JVM 实现中 ( 例如 HotSpot), 本地方法栈和虚拟机栈是一起的4.堆 (Heap) : JVM 所管理的最大内存区域 . 使用 new 创建的对象都是在堆上保存 ( 例如前面的 new int[]{1, 2, 3} ) , 堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销 毁 。5.方法区 (Method Area) : 用于 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数 据 . 方法编译出的的字节码就是保存在这个区域
2.2 基本类型变量与引用类型变量的区别
public static void func() {
int a = 10;
int b = 20;
int[] arr = new int[]{1,2,3};
}
2.3 再谈引用变量
public static void func() {
int[] array1 = new int[3];
array1[0] = 10;
array1[1] = 20;
array1[2] = 30;
int[] array2 = new int[]{1,2,3,4,5};
array2[0] = 100;
array2[1] = 200;
array1 = array2;
array1[2] = 300;
array1[3] = 400;
array2[4] = 500;
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i]);
}
}
1.创建数组array1,没有给数组元素设置初始值,因此每个位置都是0
2.通过下标的方式将数组中每个元素修改成10、20、30
3.创建数组arra2,并将其中元素设置为1、2、3、4、54.通过下标方式将数组中前两个元素设置为100、200
1.array1=array2,即让array1去引用array2引用的数组的空间,此时array1和array2实际是一个数组2.通过array1将数组2和3号位置元素修改为300、400,此时array2也也能看到数组中修改的结果,因为array1和array2引用的是同一个数组3.通过array2将数组4号位置元素修改为500,此时array1也能看到数组中修改的结果,因为array1和array2引用的是同一个数组4.通过array2对数组中元素进行打印,输出100、200、300、400、500
2.4 认识 null
null 在 Java 中表示 "空引用" , 也就是一个不指向对象的引用.
int[] arr = null;
System.out.println(arr[0]);
// 执行结果
Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:6)
注意 : Java 中并没有约定 null 和 0 号地址的内存有任何关联 .
3. 数组的应用场景
3.1 保存数据
public static void main(String[] args) {
int[] array = {1, 2, 3};
for(int i = 0; i < array.length; ++i){
System.out.println(array[i] + " ");
}
}
3.2 作为函数的参数
1. 参数传基本数据类型
public static void main(String[] args) {
int num = 0;
func(num);
System.out.println("num = " + num);
}
public static void func(int x) {
x = 10;
System.out.println("x = " + x);
}
// 执行结果
x = 10
num = 0
发现在func方法中修改形参 x 的值, 不影响实参的 num 值.
public static void main(String[] args) {
int[] arr = {1, 2, 3};
func(arr);
System.out.println("arr[0] = " + arr[0]);
}
public static void func(int[] a) {
a[0] = 10;
System.out.println("a[0] = " + a[0]);
}
// 执行结果
a[0] = 10
arr[0] = 10
总结 : 所谓的 " 引用 " 本质上只是存了一个地址 . Java 将数组设定成引用类型 , 这样的话后续进行数组参数传参 , 其实只是将数组的地址传入到函数形参中. 这样可以避免对整个数组的拷贝 ( 数组可能比较长 , 那么拷贝开销就会很大 ).
3.3 作为函数的返回值
public class TestArray {
public static int[] fib(int n){
if(n <= 0){
return null;
}
int[] array = new int[n];
array[0] = array[1] = 1;
for(int i = 2; i < n; ++i){
array[i] = array[i-1] + array[i-2];
}
return array;
}
public static void main(String[] args) {
int[] array = fib(10);
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
}
4. 数组练习
4.1 数组转字符串
import java.util.Arrays
int[] arr = {1,2,3,4,5,6};
String newArr = Arrays.toString(arr);
System.out.println(newArr);
// 执行结果
[1, 2, 3, 4, 5, 6]
4.2 数组拷贝
import java.util.Arrays;
public static void func(){
// newArr和arr引用的是同一个数组
// 因此newArr修改空间中内容之后,arr也可以看到修改的结果
int[] arr = {1,2,3,4,5,6};
int[] newArr = arr;
newArr[0] = 10;
System.out.println("newArr: " + Arrays.toString(arr));
// 使用Arrays中copyOf方法完成数组的拷贝:
// copyOf方法在进行数组拷贝时,创建了一个新的数组
// arr和newArr引用的不是同一个数组
arr[0] = 1;
newArr = Arrays.copyOf(arr, arr.length);
System.out.println("newArr: " + Arrays.toString(newArr));
// 因为arr修改其引用数组中内容时,对newArr没有任何影响
arr[0] = 10;
System.out.println("arr: " + Arrays.toString(arr));
System.out.println("newArr: " + Arrays.toString(newArr));
// 拷贝某个范围.
int[] newArr2 = Arrays.copyOfRange(arr, 2, 4);
System.out.println("newArr2: " + Arrays.toString(newArr2));
}
4.3 求数组中元素的平均值
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(avg(arr));
}
public static double avg(int[] arr) {
int sum = 0;
for (int x : arr) {
sum += x;
}
return (double)sum / (double)arr.length;
}
// 执行结果
3.5
4.4 查找数组中指定元素(顺序查找)
public static void main(String[] args) {
int[] arr = {1,2,3,10,5,6};
System.out.println(find(arr, 10));
}
public static int find(int[] arr, int data) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == data) {
return i;
}
}
return -1; // 表示没有找到
}
// 执行结果
3
4.5 查找数组中指定元素(二分查找)
啥叫有序数组 ?有序分为 " 升序 " 和 " 降序 "如 1 2 3 4 , 依次递增即为升序 .如 4 3 2 1 , 依次递减即为降序 .
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(binarySearch(arr, 6));
}
public static int binarySearch(int[] arr, int toFind) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (toFind < arr[mid]) {
// 去左侧区间找
right = mid - 1;
} else if (toFind > arr[mid]) {
// 去右侧区间找
left = mid + 1;
} else {
// 相等, 说明找到了
return mid;
}
}
// 循环结束, 说明没找到
return -1;
}
// 执行结果
5
4.6 数组排序(冒泡排序)
public static void main(String[] args) {
int[] arr = {9, 5, 2, 7};
bubbleSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 1; j < arr.length-i; j++) {
if (arr[j-1] > arr[j]) {
int tmp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = tmp;
}
}
} // end for
} // end bubbleSort
// 执行结果
[2, 5, 7, 9]
4.7 数组逆序
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4};
reverse(arr);
System.out.println(Arrays.toString(arr));
}
public static void reverse(int[] arr) {
int left = 0;
int right = arr.length - 1;
while (left < right) {
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left++;
right--;
}
}
5. 二维数组
数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };
int[][] arr = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
for (int row = 0; row < arr.length; row++) {
for (int col = 0; col < arr[row].length; col++) {
System.out.printf("%d\t", arr[row][col]);
}
System.out.println("");
}
// 执行结果
1 2 3 4
5 6 7 8
9 10 11 12