Java数组(详解版)
数组的定义:
什么是数组:
数组:可以看成是相同类型元素的一个集合。在内存中是一段连续的空间。
1. 数组中存放的元素其类型相同2. 数组的空间是连在一起的3. 每个空间有自己的编号,其实位置的编号为 0 ,即数组的下标。
数组的创建及初始化:
数组的创建:
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 个字符串元素的数组
当然这样创建的数组,其实也顺便将其初始化了,内部将其每个位置的元素初始化为0.
数组的初始化:
动态初始化:
int [] array = new int [ 10 ];
此时默认将10个元素初始化为0。
具体不同类型的数组默认初始化的默认值是有区别的:
静态初始化:
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" , "!!!" };
注意:1、静态初始化虽然 没有指定数组的长度 ,编译器在 编译时会根据{}中元素个数来确定数组的长度。2、静态初始化时 , {} 中数据类型必须与 [] 前数据类型一致。3、 静态初始化可以简写,省去后面的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" , "!!!" };
*数组的使用:
数组的访问:
这里和C与语言一样都是通过下标访问:
int [] array = new int []{ 1 , 2 , 3 , 4 , 5 };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 ] = 10 ;System . out . println ( array [ 0 ]);
注意:
1. 数组是一段连续的内存空间,因此 支持随机访问,即通过下标访问快速访问数组中任意位置的元素2. 下标从0开始,介于[0, N)之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。
如果遇到二的情况:
在Idea中会报:
数组越界的信息!
数组的遍历:
普通方式:
我们可以和C语言中一样,利用for循环进行遍历:
for(int i = 0;i< arr2.length;i++){ System.out.print(arr2[i]+" "); }
注意:
这里求数组的长度用到了一个Java自带的一个方法,数组名.length。
加强版for循环遍历数组:
可以使用 for-each 遍历数组:
for(int x:arr2){ System.out.print(x+" "); }//int x 表示arr2中的每一个元素可以用int 类型的基本数据类型接收。
for-each 是 for 循环的另外一种使用方式. 能够更方便的完成对数组的遍历. 可以避免循环条件和更新语句写错。
数组是引用类型:
内存的划分:
Java虚拟机运行时,JVM中的内存被划分为5个部分:
程序计数器 (PC Register): 只是一个很小的空间 , 保存下一条执行的指令的地址。虚拟机栈 (JVM Stack): 与方法调用相关的一些信息, 每个方法在执行时,都会先创建一个栈帧 ,栈帧中包含 有:局部变量表 、 操作数栈 、 动态链接 、 返回地址 以及其他的一些信息,保存的都是与方法执行时相关的一 些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了 。本地方法栈 (Native Method Stack): 本地方法栈与虚拟机栈的作用类似 . 只不过 保存的内容是 Native 方法的局 部变量 . 在有些版本的 JVM 实现中 ( 例如 HotSpot), 本地方法栈和虚拟机栈是一起的堆 (Heap) : JVM 所管理的最大内存区域 . 使用 new 创建的对象都是在堆上保存 ( 例如前面的 new int[]{1, 2, 3} ) , 堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销 毁 。方法区 (Method Area) : 用于 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数 据 . 方法编译出的的字节码就是保存在这个区域
引用类型与基本数据类型的区别:
public static void func () {int a = 10 ;int b = 20 ;int [] arr = new int []{ 1 , 2 , 3 };}
Java的数组的开辟与C语言不同,C语言数组的开辟都是在栈中开辟,但是Java数组的开辟是
在堆区开辟,虚拟机栈存储堆区开辟数组对象的地址。
C语言中数组的开辟:
引用变量:
通过上述的分析:
Java中的数组是一种引用,我们可以理解为存储数组的地址的变量就称为引用变量:
例如:
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 ]);}}
看到加红的地方,array2直接能传给array1,这里的含义就是将array2保存的数组的数组的地址传给array1,同时能让array1也可以访问arrat2指向的数组。
画图分析:
对null的理解:
Java中没有大写的NULL,只有小写的null。
null 在 Java 中表示 "空引用" , 也就是一个不指向对象的引用.
int [] arr = null ;System . out . println ( arr [ 0 ]);// 执行结果Exception in thread "main" java . lang . NullPointerExceptionat Test . main ( Test . java : 6 )
数组的应用:
作为函数的参数:
这次如果想要交换两个值,可以把两个值存在数组里面进行交换:
public static void swap(int[] arr){
int tmp = arr[1];
arr[1] = arr[0];
arr[0] = tmp;
}
public static void main(String[] args) {
int[] arr = {3,4};
swap(arr);
for(int i = 0;i< arr.length;i++){
System.out.print(arr[i]+" ");
}
发现在swap 方法内部修改数组的内容 , 方法外部的数组内容也发生改变 .因为数组是引用类型,按照引用类型来进行传递, 是可以修改其中存放的内容的 。
作为函数的返回值:
计算斐波那契数。
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.print(array[i]+" ");
}
}
}
有关数组的方法使用:
Java 中提供了 java.util.Arrays 包, 其中包含了一些操作数组的常用方法:
数组转字符串:
import java . util . Arraysint [] arr = { 1 , 2 , 3 , 4 , 5 , 6 };String newArr = Arrays . toString ( arr );System . out . println ( newArr );// 执行结果[ 1 , 2 , 3 , 4 , 5 , 6 ]
模拟实现Arrary.toString().
public static void mytoString(int[] arr) {
System.out.print("[");
for (int i = 0; i <arr.length ; i++) {
System.out.print(arr[i]+"");
if(i != arr.length-1){
System.out.print(",");
}
}
System.out.print("]");
}
数组的拷贝:
for循环拷贝:
public static void main(String[] args) {
int[] arr1 = {1,2,3,4,5,6};
int[] arr2 = new int[6];
for(int i = 0;i<arr1.length;i++) {
arr2[i] = arr1[i];
}
}
使用Arrays中copyOf方法完成数组的拷贝:
public static void main(String[] args) { int[] arr1 = {1,2,3,4,5,6}; int[] arr2 = null; arr2 = Arrays.copyOf(arr1,arr1.length); arr1[2] = 20;//为了验证是否真正拷贝了。 System.out.println(Arrays.toString(arr2));//为了打印方便,将其转换为字符串打印 }
拷贝某个范围:
拷贝某个范围的话,可以直接使用Arrays.copyOfrange();
public static void main(String[] args) { int[] arr1 = {1,2,3,4,5,6}; int[] arr2 = null; //arr2 = Arrays.copyOf(arr1,2*arr1.length); arr2 = Arrays.copyOfRange(arr1,2,5);//注意拷贝的下标为[2,5)左闭右开 arr1[2] = 20; System.out.println(Arrays.toString(arr2)); }
求数组中所有元素的平均值:
double sum = 0; for(int x:arr1){ sum+=x; } double average = (double)sum/arr1.length; System.out.println(average); }
数组的二分查找:
public static int binary(int[] arr,int k){ int mid = arr.length/2; int left = 0; int right = arr.length-1; while(left<right) { if(k>arr[mid]) { left = mid+1; } else if(k == arr[mid]) { return mid; } else { right = mid-1; } } return -1; }
数组的逆序:
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--; } }
上面的数组的练习需要做自己多来练,如果在C语言阶段练习过,还是建议在Java中在次练习!