理解数组
数组也是一种数据类型, 它本身是一种引用类型。
1.什么是数组:存储相同数据类型的容器
2.特点:
Java 的数组既可以存储基本类型的数据 ,也可以存储引用类型的数据,只要所有的数组元素具有相同的类型即可;
有序的(下标,从0开始);
一旦数组的初始化完成,数组在内存中所占的空间将被固定下来,因此数组的长度将不可改变。 (属性 length)即使把某个数组元素的数据清空 ,但它所占的空间依然被保留 ,依然属于该数组,数组的长度依然不变。
定义数组
数据元素的类型[] 数组名;
int[] nums;
int nums[];
数组是一种引用类型的变量, 因此使用它定义一个变量时,仅仅表示定义了一个引用变量( 也就是定义了一个指针),这个引用变量还未指向任何有效的内存, 因此定义数组时不能指定数组的长度。 而且由于定义数组只是定义了一个引用变量,并未指向任何有效的内存空间 ,所以还没有内存空间来存储 数组元素,因此这个数组也不能使用,只有对数组进行初始化后才可以使用。
创建数组(申请内存)
只要为数组元素分配了内存空间,数组元素就有了初始值。
数组的初始化有如下两种方式。
静态初始化: 初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度。
动态初始化:初始化时程序员只指定数组长度,由系统为数组元素分配初始值。
1. 静态初始化
静态初始化的语法格式如下:
arrayName = new type[] {el ementl, element2 , element3 , element4 .. . }
除此之外,静态初始化还有如下简化的语法格式:
type[] arrayName = {elementl, element2, element3, element4 . . . }
2. 动态初始化
动态初始化的语法格式如下:
arrayName = new type[length] ;
数组初始化完成后,就可以使用数组了 ,包括为数组元素赋值、访问数组元素值和获得数组长度等。
存放数据
// 为 nums 的第一个数组元素赋值
nums[0]=1;
nums[9]=2;
使用数组
// 输出 nums 数组的第1个元素
System.out.println(nums[0]);
for(int i=0;i<nums.length;i++){
System.out.println(nums[i]);
}
增强for循环(foreach 循环)
for(int i : nums){
System.out.println(i);
}
//数组变成字符串
Arrays.toString(nums);
int nums[];
nums=new int[10];
nums[0]=1;
nums[9]=2;
//数组变成字符串
System.out.println(Arrays.toString(nums)); //[1, 0, 0, 0, 0, 0, 0, 0, 0, 2]
for(int i=0;i<nums.length;i++) {
//对数组中的元素赋值
nums[i]=90;
System.out.println(nums[i]); //结果为10个 90
}
//测试数组中的元素是否真的被赋值
System.out.println("aaa"+nums[2]); //aaa90,所以这种写法可给数组元素赋值
for(int n:nums) {
n=80;
System.out.println(n); //结果为10个 80
}
System.out.println("未被赋值"+nums[2]); //未被赋值0
从上面程序看, 当使用 foreach 来迭代访问数组元素时, foreach 中的循环变量相当于一个临时变量, 系统会把数组元素依次赋给这个临时变量,而这个临时变量并不是数组元素, 它只是保存了数组元素的值。因此,如果希望改变数组元素的值,则不能使用这种 foreach 循环。
main方法的args数组的使用
public static void main(String[] args) {
//练习:求数组元素的和
//Integer.parseInt(args[i]);
int sum=0;
for(int i=0;i<args.length;i++) {
int n=Integer.parseInt(args[i]);
sum+=n;
System. out .println(n+" ");
}
System. out .println("求和为:"+sum);
//System. out .println(Arrays.toString(args));
}
结果如下
1
2
3
4
求和为:10
数组的拷贝和扩容
Arrays.toString(type[] a): 该方法将一个数组转换成一个字符串.
int [] a= {1,2,3,4,5};
int [] b= new int[10];
//数组的拷贝
//b= {1,2,3,0,0......};
//arraycopy
//第一个参数:原数组
//第二个参数:原数组元素的起始位置
//第三个参数:目标数组
//第四个参数:目标数组元素的起始位置
//第五个参数:长度
// System.arraycopy(a, 0, b, 0, 3);
// System.out.println(Arrays.toString(b));
//练习:b={0,0,3,4,0...}
System.arraycopy(a, 2, b, 2, 2);
System.out.println(Arrays.toString(b));
数组扩容(实际创建了一个新的数组),具体看下图
//数组扩容(实际创建了一个新的数组)
//copyOf
//第一个参数:原数组(拷贝)
//第二个参数:扩容之后的长度
String[] names= {"张三"};
names[1]= "李四";
// 访问数组元素指定的索引值等于数组长度,所以下面代码将在运行时出现异常
System.out.println(names[1]);
如果访问数组元素时指定的索引值小于 0, 或者大于等于数组的长度,编译程序不会出现任何错误, 但运行时出现异常: java.lang.ArrayIndexOutOtBoundsException: N (数组索引越界异常),
names=Arrays.copyOf(names, names.length+1);
names[1]="李四";
//结果为[张三, 李四]
System.out.println(Arrays.toString(names));
内存:堆,栈 ,方法区,本地方法栈,寄存器
栈存局部变量,引用变量
堆存对象
jvm的线程垃圾回收
为什么有栈内存和堆内存之分?
当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块内存栈里,随着执行方法的结束,这个方法的内存栈也将自然销毁。因此,所有在方法中定义的局部变量都是放在栈内存中的;在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用,这个运行时数据就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用,则这个对象不会被销毁。只有当一个对象没有任何引用变量引用它时,系统的垃圾回收器才会在合适的时候回收它。
提示:看待一个数组时, 一定要把数组看成两个部分:一部分是数组引用,也就是在代码中定义的数组引用变量;还有一部分是实际的数组对象,这部分是在堆内存里运行的,通常无法直接访问它,只能通过数组引用变量来访问 。
数组元素的排序
1.冒泡排序
两两比较,大的沉下去,小的浮上来,从小到大
图片来自这里.
例题:对23,56,22,15排序
第一轮排序 :
结果为23,22,15,56。交换了3次
过程如下
23,56
22,56
15,56
第二轮排序 :
结果为22,15,23,交换了2次
过程如下
22,23
15,23
第三轮排序 :
结果为15,22。交换了1次
用Java代码实现:
int[] num= {23,56,22,15};
int x;
//进行了三轮排序
for(int i=0;i<3;i++) {
//交换次数
for(int j=0;j<num.length-1-i;j++) {
if(num[j]>num[j+1]) {
x=num[j];
num[j]=num[j+1];
num[j+1]=x;
}
}
}
//结果为[15, 22, 23, 56]
System.out.println(Arrays.toString(num));
2.选择排序:
用Java代码实现:
for(int i=0;i<num.length-1;i++) {
for(int j=i+1;j<num.length;j++) {
if(num[i]<num[j]) {
int x=num[i];
num[i]=num[j];
}
}
}
System.out.println(Arrays.toString(num));
除上述之外,排序的方法Arrays.sort(num);也可以排序。
二维数组
1.声明:
int[][] arrays;
//第一个[] 表示行,第二个表示列
int[] arrays[];
int arrays[][];
int[] a[],b; //a二维,b一维.两者不包含
int[] c=new int[2];
int [][] d=new int[2][3];
//只有相同数据类型才能赋值
a=c; //二维数组赋给一维数组 error
b=c; //ok
a=d; //error,因为a只是声明,d有指向内存
b=d; //error ??????
a[0]=c; //a是二维,但是a[0]代表第一行,是一维数组
a[0]=d; //error
2.创建:
arrays=new int[2][3];//2行3列
3.赋值:
arrays[0][2]=1;
结果为:
0 0 1
0 0 0
获取数组元素,使用嵌套循环
//二维数组名.length得到的是二维数组的行
int[][] nums;
nums=new int[2][3];
nums[1][1]=1;
for(int i=0;i<nums.length;i++){
for(int j=0;j<nums[i].length;j++){
System.out.print(nums[i][j]+"\t");
}
System.out.println();
}
结果为:
0 0 0
0 1 0
二维数组的其他定义方式:
//二维数组的其他定义方式
int[][] nums=new int[2][3];
int[][] nums2= {{1,2,3},{4,5,6}};
int[][] nums3=new int[][]{{1,2,3},{4,5,6}};
不规则的二维数组的定义
//int[][] nums=new int[][3]; error
//int[][] nums=new int[][]; error
int[][] nums=new int[3][]; //3行
nums[0]=new int[3];
nums[1]=new int[1];
nums[2]=new int[5];
for(int i=0;i<nums.length;i++){
for(int j=0;j<nums[i].length;j++){
System.out.print(nums[i][j]+"\t");
}
System.out.println();
}
结果为:
0 0 0
0
0 0 0 0 0
练习题
1.对角线求和
1 2 3 1 [0][0] 3 [0][2]
1 2 2 2 [1][1] 2 [1][1]
3 2 2 2 [2][2] 3 [2][0]
public static void test() {
int[][] nums=new int[][]{{1,2,3},{1,2,2},{3,2,2}};
int sum=0;
for(int i=0;i<nums.length;i++){
for(int j=0;j<nums[i].length;j++){
if(i==j||i+j==2) {
sum+=nums[i][j];
}
}
}
System.out.print(sum);
}
2.矩阵转置
1 2 3 [0][0] [0][1] [0][2]
4 5 6 [1][0] [1][1] [1][2]
1 4 [0][0] [0][1]
2 5 [1][0] [1][1]
3 6 [2][0] [2][1]
public static void test4() {
int[][] nums=new int[][]{{1,2,3},{4,5,6}};
int[][] nums2=new int[3][2];
for(int i=0;i<nums.length;i++){
for(int j=0;j<nums[0].length;j++){
nums2[j][i]=nums[i][j];
}
}
for(int i=0;i<nums2.length;i++){
for(int j=0;j<nums2[i].length;j++){
System.out.print(nums2[i][j]+"\t");
}
System.out.println();
}
}
3.杨辉三角
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
public static void test3() {
Scanner sc=new Scanner(System.in);
System.out.println("input n:");
int n =sc.nextInt();
int[][] nums = new int[n][];
for(int i=0;i<nums.length;i++){
nums[i]=new int[i+1];
for(int j=0;j<nums[i].length;j++){
if(j==0||i==j) {
nums[i][j]=1;
}else {
nums[i][j]=nums[i-1][j-1]+nums[i-1][j];
}
}
}
for(int i=0;i<nums.length;i++){
for(int j=0;j<nums[i].length;j++){
System.out.print(nums[i][j]+"\t");
}
System.out.println();
}
}
特点:每一行的开始和结束都是1;其余位置的元素是计算:
arr[i][j] = arr[i - 1][j] + arr[i - 1][j - 1];
输入一个数字n表示行数,输出对应的前n行