目录
一. 数组的基本概念
1.什么是数组
2.数组的创建及初始化
2.1数组的创建
T [] 数组名 = new T [ N ];T :表示数组中存放元素的类型T[] :表示数组的类型N :表示数组的长度
2.2 数组的初始化
(1)动态初始化
int[] array3=new int[10];
(2) 静态初始化
在创建数组时不直接指定数据元素个数,而直接将具体的数据内容进行指定
语法格式: T[] 数组名称 = {data1, data2, data3, ..., datan};
double[] array2 = new double[]{1.0, 2.0, 3.0, 4.0, 5.0};
String [] array3 = new String []{ "hell" , "Java" , "!!!" }
- 静态初始化虽然没有指定数组的长度,编译器在编译时会根据{}中元素个数来确定数组的长度。
- 静态初始化时, {}中数据类型必须与[]前数据类型一致。
- 静态初始化可以简写,省去后面的new T[]。
int[] array={1,2,3,4,5}; int[] array2=new int[]{1,2,3,4,5};这两种写法是完全等效的!!!
Tip:
1.静态和动态初始化也可以分为两步,但是省略格式不可以。
int[] array4; array4=new int[10];int[] array5; array5=new int[]{1,2,3,4,5};// 注意省略格式不可以拆分 , 否则编译失败//int[] array6; //array6={1,2,3,4,5};
如果数组中存储元素类型为引用类型,默认值为null
3.数组的使用
3.1数组中元素访问
for (int i = 0; i < array.length; i++) { System.out.print(array[i]+""); }
【注意事项】
System.out.println("===普通for循环==="); for (int i = 0; i < array.length; i++) { System.out.print(array[i]+" "); }
System.out.println("===for each==="); for (int e: array) { System.out.print(e+" "); }
System.out.println("===操作数组的工具类Arrays==="); String ret= Arrays.toString(array); System.out.println(ret);
可知,其返回值类型为String
使用 Arrays的toString 方法,把传入的参数array表示的数组,以字符串的形式进行输出!!!
二、数组是引用类型
1 初始JVM的内存分布
几个问题:1.为什么划分内存?答:如果对内存中存储的数据不加区分的随意存储,那对内存管理起来将会非常麻烦。比如:2.划分成了哪些内存?JVM 对所使用的内存按照功能的不同进行了划分:
- 程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址
- 虚拟机栈(JVM Stack): 与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。
- 本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的
- 堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2, 3} ),堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
- 方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域
现在我们只简单关心堆和虚拟机栈这两块空间,后序 JVM 中还会更详细介绍。
3.为什么会有2个栈?本地方法栈:会执行一些底层是由C/C++代码 实现的方法。
Java虚拟机栈:"平时嘴里说的栈”(局部变量)
2 基本类型变量与引用类型变量的区别
public static void fun(){ int a=10; int b=20; int[] array={1,2,3}; }
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 ]);}}//array1这个引用 指向array2这个引用所指向的对象//当 2个引用同时指向一个对象的时候 ,通过任何一个引用都能修改这个对象的值
问题:这个array1去哪里了?“
答:如果这个对象一直没有人引用他,此时,就会被JVM自动回收 。
4.认识 null
null 在 Java 中表示 "空引用" , 也就是一个不指向对象的引用.
int[] array2=null;
array2这个引用不指向任何对象
null/. xxx
总结:
- 如果将来代码报错 “空指针异常”,我们要做的是找到这行代码中的引用。 判断这个引用为什么是空的!!
- null 的作用类似于 C 语言中的 NULL (空指针), 都是表示一个无效的内存位置. 因此不能对这个内存进行任何读写操作. 一旦尝试读写, 就会抛出 NullPointerException.
- Java 中并没有约定 null 和 0 号地址的内存有任何关联.
三、数组的应用
1.作为返回值
题目:把数组中每个数据扩大2倍
//需求:把数组中每个数据扩大2倍
public static int[] fun(int[] array){
int[] ret=new int[array.length];
for (int i = 0; i < array.length ; i++) {
ret[i]=array[i]*2;
}
return ret;//函数结束:意味着回收局部变量的内存
}
public static void main(String[] args) {
int[] array={1,2,3,4,5};
int[] tmp=fun(array);
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(tmp));
}
2.参数传数组类型(引用数据类型)
public static void fun(int[] array){
for (int i = 0; i < array.length ; i++) {
array[i]=array[i]*2;
}
}
public static void main(String[] args) {
int[] array={1,2,3,4,5};
System.out.println(Arrays.toString(array));
fun(array);
System.out.println(Arrays.toString(array));
}
总结: 所谓的 "引用" 本质上只是存了一个地址. Java 将数组设定成引用类型, 这样的话后续进行数组参数传参, 其实只是将数组的地址传入到函数形参中. 这样可以避免对整个数组的拷贝(数组可能比较长, 那么拷贝开销就会很大)
问题:是不是传引用就能够修改实参的值?
一个代码说明问题
public static void fun3(int[] array){ array[0]=99; } public static void fun4(int[] array){ array=new int[]{99,2,3,4,5};//自己指向新对象 } public static void main(String[] args) { int[] array={1,2,3,4,5}; System.out.println(Arrays.toString(array)); fun4(array); System.out.println(Arrays.toString(array)); fun3(array); System.out.println(Arrays.toString(array)); }
总结:
1.int[0] array = null; 代表 这个引用 不指向任何对象
2.array1 = array2 ;代表 array1这个引用 指向了 array2这个引用 指向的对象
3.一个引用 不可能同时指向2个对象
四、数组练习
1.数组转字符串
public static String toMyString(int[] array){
String string="";
string+="[";
for (int i = 0; i < array.length-1; i++) {
string+=array[i]+",";
}
string+=array[array.length-1];
string+="]";
return string;
}
public static void main(String[] args) {
int[] array={1,2,3,4,5};
String string=toMyString(array);
System.out.println(string);
}
2.查找数组中指定元素(顺序查找)
//挨个查找,效率比较低
public static int findNum(int[] array,int key){
int index=-1;
for (int i = 0; i < array.length ; i++) {
if(array[i]==key){
index=i;
}
}
return index;
}
public static void main(String[] args) {
int[] array={1,2,3,4,5};
int index=findNum(array,5);
System.out.println(index);
}
3.查找数组中指定元素(二分查找)
以升序数组为例 , 二分查找的思路是先取中间位置的元素 , 然后使用待查找元素与数组中间元素进行比较:如果相等,即找到了返回该元素在数组中的下标如果小于,以类似方式到数组左半侧查找如果大于,以类似方式到数组右半侧查找
//有序时,二分查找
public static void main(String[] args) {
int[] array={1,2,3,4,5};
Arrays.sort(array);//排序算法
int index=binarySearch(array,5);
System.out.println(index);
}
public static int binarySearch(int[] array,int key){
int left=0;
int right=array.length-1;
while(left<=right){
int mid=(left+right)/2;
if(array[mid]==key){
return mid;
}else if(key<array[mid]){
right=mid-1;
}else{
left=mid+1;
}
}
return -1;
}
Java自己也有Arrays.binarySearch()方法
底部源码:
4.数组排序(冒泡排序)
public static int[] bubbleSort(int[] array,int key){
for (int i = 0; i < array.length-1; i++) {
boolean flag=false;
for (int j = 0; j <array.length-1-i ; j++) {
if(array[j]<array[j+1]){
int tmp=array[j];
array[j]=array[j+1];
array[j+1]=tmp;
flag=true;
}
}
if(flag==false){
break;
}
}
return array;
}
5.数组拷贝
public static void main(String[] args) {
int[] array={1,2,3,4,5};
int[] newArray=array;
newArray[0]=10;
// newArr和arr引用的是同一个数组
// 因此newArray修改空间中内容之后,arr也可以看到修改的结果
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(newArray));
}
使用 Arrays 中 copyOf 方法完成数组的拷贝:
arr [ 0 ] = 1 ;newArr = Arrays . copyOf ( arr , arr . length );//将后面大小变大则相当于也扩容拷贝的数组 新的数组长度System . out . println ( "newArray: " + Arrays . toString ( newArr ));
五、几个数组常用方法
1.Arrays.toString()
2.Arrays.equals()
3.Arrays.fill()
4.Arrays.copyOf()
六、二维数组
1.二维数组形式
数据类型 [][] 数组名称 = new 数据类型 [ 行数 ][ 列数 ] { 初始化数据 };
public static void main(String[] args) {
int[][] array=new int[][]{{1,2,3},{4,5,6}};
int[][] array2={{1,2,3},{4,5,6}};
System.out.println(array.length);//行数
System.out.println(array[0].length);//第0行有几列
System.out.println(array[1].length);//第1行有几列
System.out.println(array);//首元素地址
System.out.println(array[0]);//第0行首元素地址
System.out.println(array[1]);//第1行首元素地址
}
2.遍历二维数组
for (int i = 0; i < array.length ; i++) {
for (int j = 0; j <array[i].length ; j++) {
System.out.print(array[i][j]+" ");
}
}
System.out.println();
System.out.println("=================");
for (int[] i:array) {
for (int x:i) {
System.out.print(x+" ");
}
}
3.二维数组转字符串
String ret=Arrays.toString(array);
System.out.println(ret);//地址
System.out.println("=================");
String rett=Arrays.deepToString(array);
System.out.println(rett);//真的转化成字符串
4.不规则二维数组
public static void main(String[] args) {
int[][] array=new int[2][];
for (int i = 0; i <array.length ; i++) {
for (int j = 0; j <array[i].length ; j++) {
System.out.println(array[i][j]+" ");
}
}
}
进行遍历会报错
原因:
数组的行所对应的列是不固定的