Java学习笔记4.——数组
Java基础学习笔记,数组篇
文章目录
前言
Java基础=计算机基础知识+Java基础语法+运算符+流程控制语句+数组,现在学数组。
提示:以下是本篇文章正文内容,下面案例可供参考
一、数组是什么?
数组,顾名思义,是把数字装起来的东西,不管里面有多少个都称为一组,所以数组英文名叫array。
每一组里面存放的数据类型相同,因为方便存取,看名字也知道存的是什么,计算机也能提高存储效率和运行效率。其实我在想最开始的时候设计的时候是不是与电子有关,只是懂的太少也不知道从何思考,先留个疑问。
1.数组的定义
- 数据类型 [] 数组名;
- int[] array;
- 数组类型 数组名[];
- int array[];
1.数组初始化的两种方式
初始化就是在内存中,为数组容器开辟空间,并将数据存入容器中的过程。
静态初始化
动态初始化
1.静态初始化
完整格式 数组类型[] 数组名 = new 数组类型{元素1,元素2,…};
因为数组中的元素是确定的个数,所以一开始申请就知道需要申请多大的空间,一旦申请好了这个空间就不变化了,然后也同时创建了这个空间的地址值。
那当我们去找这个数组时就需要去访问内存,然后内存通过地址来找这个数组,我们可以直接输出数组他就会打印数组的地址
double heights[] = {};
System.out.println(age);
//当然定义这样的数组是没有什么用处的,里面包含了null元素。
double height[]=new double[0];//创建的是一个没有元素但是长度为0的数组,也不可以添加一个数据
输出结果为[I@10f87f48
[ :代表数组
I :代表int整型,如果是D就是double类型
@ :一个固定格式
10f87f48才是真正的地址值,16进制表示。
注(百度的):然而,当数组的元素类型是对象弓用类型(例如String、 ArrayList等) 时,初始化时必须为数组元素指定一个或多个对象引|用,否则数组元素的默认值将是null。因此,在Java中, int[] a = {}或int[] a = new int[0]不会将数组a的元素初始化为null,因为int不是对象引用类型。
又看了些文章,空数组代表一个对象,数组为0的对象,长度为0;
int [] a= null;
这是空的,就是什么都没有。问了下ai这种在Java中是一种错误的语法,额什么都没有。
为什么直接输出数组名他就会打印数组的地址
因为数组是一个抽象概念,实际存储在内存中,数组名只是一个指向数组在内存中位置的指针。使用数组名的时候就是在访问和操作存储在内存中的数据,所以要查看或者显示数组中的所有数据需要遍历或者指定比如:heights[1];1就是索引
索引的特点从0开始,因为在以前设计之初的时候人们都常从0开始计数。
数组空间里的数据就是通过索引取出来的,如果里面的值都类似于存放常量的区,但是只是类似。
如果数组中的某个值被覆盖了,那原本那个值就不存在了。
数组地址和整型地址的区别
既然数组有地址,那么整型变量在内存中也会有地址,可以通过变量名来引用他所对应的地址,编译器自动管理变量的内存地址所以我们对地址就了解得很陌生
整数变量是单个数据的存储,数组的多个数据的存储,数组中每个元素都是连续存储的,也都有自己的地址。
只不过数组地址代表那个地方还有很多地址,不能像变量一样使用,还需要指定。
数组遍历
数组遍历:取出所有数据的过程
public class ArrayTest1 {
public static void main(String[] args) {
int [] age = new int[]{1,2,3,4,5};
for (int i = 0; i < age.length; i++) {
System.out.println(age[i]);
}
}
}
数组遍历小案例
遍历数组求和,
public class ArrayTest1 {
public static void main(String[] args) {
int [] age = new int[]{1,2,3,4,5};
int sum =0;
for (int i = 0; i < age.length; i++) {
sum += age[i];
}
System.out.println(sum);
}
}
统计个数
定义一个数组,存储1,2,3,4,5,6,7,8,9,10
遍历数组得到每一个元素,统计数组里面一共有多少个能被3整除的数字.
public class ArrayTest1 {
public static void main(String[] args) {
// 统计个数
//定义一个数组,存储1,2,3,4,5,6,7,8,9,10
//遍历数组得到每一个元素,统计数组里面一共有多少个能被3整除的数字.
int [] age = new int[]{1,2,3,4,5,6,7,8,9,10};
int count =0;
for (int i = 0; i < age.length; i++) {
if (age[i]%3==0){
count++;
}
}
System.out.println(count);
}
}
变化数据
定义一个数组,存储1,2,3,4,5,6,7,8,9,10
遍历数组得到每-一个元素。
要求:
1,如果是奇数,则将当前数字扩大两倍
2,如果是偶数,则将当前数字变成二分之-
public class ArrayTest1 {
public static void main(String[] args) {
// 变化数据
//定义一个数组,存储1,2,3,4,5,6,7,8,9,10
//遍历数组得到每-一个元素。
//要求:
//1,如果是奇数,则将当前数字扩大两倍
//2,如果是偶数,则将当前数字变成二分之-
int [] age = new int[]{1,2,3,4,5,6,7,8,9,10};
int count =0;
for (int i = 0; i < age.length; i++) {
if (age[i]%2==0){
age[i] = age[i]/2;
}else {
age[i] *= 2;
}
System.out.println(age[i]);
}
}
}
一个循环尽量只做一件事情,所以打印这个事情可以单独放一个循环里来做。
2.动态初始化
格式:
数据类型[] 数组名 = new 数据类型[数组长度];
int[] a = new int[5];
这种时候是不知道具体的值,但是知道数组的大小就可以使用这种,这时候虚拟机给出默认的初始化值,就不需要我们手动的初始化,但是静态的初始化我们是手动的初始化值。
数组默认初始化的规律
整数类型:默认初始化值0
小数类型:默认初始化值0.0
字符类型:默认初始化值’/u0000’空格
布尔类型:默认初始化值false
引用数据类型:默认初始化值 null
2.数组常见问题
数组索引越界
二、数组练习
1.求和
求数组里的最大值,同理最小值一样
public class ArrayTest2 {
public static void main(String[] args) {
int [] a = new int[]{33,5,22,44,55};
int max=a[0];
//因为i=0时第一次是自己跟自己比较,效率偏低没有最大化用到极致
for (int i = 1; i < a.length; i++) {
if (a[i]>max){
max = a[i];
}
}
System.out.println(max);
}
}
2.求平均值
需求:生成10个1~100之间的随机数存入数组,求他们的平均值
public class ArrayTest3 {
public static void main(String[] args) {
//遍历数组求和
//需求:生成10个1~100之间的随机数存入数组,求他们的平均值。
//分析1:数组大小10,随机数存入
int array[] = new int[10];
Random r = new Random();
for (int i = 0; i < array.length; i++) {
int a = r.nextInt(100)+1;
array[i] = a;
}
//分析2:求出所有数据之和
int sum = 0;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
System.out.println(sum);
//分析3:所有数的平均数,和/10
int average = sum/10;
//分析4:统计比平均值小的个数
int count = 0;
for (int i = 0; i < array.length; i++) {
if (array[i]<average){
count++;
}
}
System.out.println(count);
}
}
3.交换数据
//需求:定义一个数组,存入1,2,3,4,5。 按照要求交换索引对应的元素。
//交换前: 1,2,3,4,5
//交换后: 5,2,3,4,1
public class ArrayTest4 {
public static void main(String[] args) {
//需求:定义一个数组,存入1,2,3,4,5。 按照要求交换索引对应的元素。
//交换前: 1,2,3,4,5
//交换后: 5,2,3,4,1
int array[] = {1,2,3,4,5};
//分析1:从前往后交换,被交换的值需要被保存
int temp = 0;
//可以改成i<j,然后去掉if,偶数的话i<j也能交换,奇数的话中间那个数就没有被交换的必要了。
for (int i = 0,j=array.length-1; i < array.length && j>=0; i++,j--) {
if (i<=j){
temp = array[j];
array[j]=array[i];
array[i]=temp;
}
}
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
}
4.随机交换数据
需求:定义一个数组,存入1~5。要求打乱数组中所有数据的顺序。
考的知识点:
知识点1:定义数组并静态初始化
知识点2:随机考的是Random函数的使用
知识点3:随机打乱,考的是索引
知识点4:用for循环实现随机,考的是for循环的机制
知识点5:交换数据,考验对数据的覆盖这一块,需要temp保存中间值
package itheima;
import java.util.Random;
public class ArrayTest5 {
public static void main(String[] args) {
//需求:定义一个数组,存入1~5。要求打乱数组中所有数据的顺序。
//1.定义数组,存入1~5
int a[] = new int[]{1,2,3,4,5};
//2.打乱数据,需要随机数0~4,因为数组只有这么长
//3.是先随机选择一个数与之交换,所以随机数在循环里
//4.按照顺序进行随机交换,可能第一轮就与第四个数据交互,第二轮就与第一个数据交换。
Random r = new Random();
for (int i = 0; i < a.length; i++) {
int b = r.nextInt(a.length);
int temp = a[i];
a[i] = a[b];
a[b] = temp;
}
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
}
}
三、数组的内存图
1.java内存分配
听说最开始堆和方法是这样的,后来方法独立出来,最后来方法区就被拆了
JDK8的时候方法区就取消了,增加了元空间,方法区中的多种功能也进行拆分放到了堆和元空间中了。
- 栈
方法运行时使用的内存,方法运行时进入栈中执行,执行完根据先进后出的规则依次释放出栈。
方法没被运行就不会使用这个栈。 - 堆
new创建的东西都存储在堆内存中,因为new时需要申请一个空间,生成的空间有空间地址,空间用完了可以释放。所以堆区作用主要是申请和释放资源。
数组内存图的使用过程可以看阿站黑马数组内存图的视频
栈中方法里创建数组就是在堆区中申请空间,所以栈中保存的是地址指向堆区,所以计算机控制器里的PC保存的就是程序流程控制语句的这种地址吧!这是直接寻找方式吧!
数组可以指向同一个堆空间,就像方法里的引值调用。
四、二维数组
二维数组就是一个盒子里面装了两个数组,两个数组分别装了不同的数据,长度也可以不同。
二维数组的定义和前面一样,也有静态初始化和动态初始化。
二维数组的维度是行和列,行一维,列一维,所以不是说是只有两个数组的才是二维数组。它的维度在于行和列。
int[][] array = new int[][]{{数据1,数据2,...},{数据x,数据y,...}};
int[][] array2 = {{数据1,数据2,...},{数据x,数据y,...}};
//这种也是允许的,只是代码第二个数组就没啥意思了。
int[][] array3 = {{1,2,3,34,5},{}};
public static void main(String[] args) {
int[][] array3 = {
{1,2,3,4},
{5,6,7,8,9,10}
};
//遍历数组,获取一维,再打印一维数组里的数据
for (int i = 0; i < array3.length; i++) {//这是一维的,长度是2
for (int j = 0; j < array3[i].length; j++) {
System.out.print(array3[i][j]+" ");
}
System.out.println();
}
}
1.二维数组在内存中的分配
因为我们知道二维数组赋值也是一个它的地址,然后这个二维数组里放了i个(第一个中括号)一维数组的地址,然后第二个中括号放的是第i个一维数组中的有j个数据。
int[][] array = new int[2][3]这就是一个二行三列的矩阵
所以后面有两种定义方法可以看看。
1.特殊情况1
//代表有两个一维数组,一维数组里还没有添加数据,情况1,此时1维数组连地址都没有,是null
int[][] array4 = new int[2][];
// int[] arr1 = {1,2,3};
// int[] arr2 = {4,5,6,7,8};
// array4[0] = arr1;
// array4[1] = arr2;
//代表第一个一维数组里面的地址是arr1的地址,所以它就会指向arr1
// for (int i = 0; i < array4.length; i++) {//这是一维的,长度是2
// for (int j = 0; j < array4[i].length; j++) {
// System.out.print(array4[i][j]+" ");
// }
// System.out.println();
// }
System.out.println(array4[0]);//这个输出结果是null
此时的状态就是这样,所以输出结果是null
第一种情况是定义之后,只定义了二维数组有几个一维数组,但是一维数组没有定义是null,这个时候创建一维数组再把地址分别赋值给二维的一维数组,这种可以自定义不用被固定在具体的矩阵行列里。
2.特殊情况2
假如赋值了,就是下面这种
int[][] array4 = new int[2][3];
代表两个一维数组的长度都是3,并且这时一维数组已经建立起来了,其值已经被系统自动初始化了,且输出System.out.println(array5[0]);结果是第0行的一维数组的地址。
遍历这个数组就是输出二行三列的0.
//情况二
int[][] array5 = new int[2][3];
int[] arr3 = {1,2};
int[] arr4 = {4,5,6,4};
//分别把这两个一维数组的地址传给二维数组
array5[0] = arr3;
array5[1] = arr4;
for (int i = 0; i < array5.length; i++) {//这是一维的,长度是2
for (int j = 0; j < array5[i].length; j++) {
System.out.print(array5[i][j]+" ");
}
System.out.println();
}
System.out.println(array5[0]);
}
这个时候一维数组的长度也是不受限制的,因为其地址值都已经变了,而原来的那个地址值也就消失了,因为没有使用的价值也没有存在的变量节约空间。
1和2的区别如下:
第一种是二维数组只是说有,但是没有定义一维数组地址值是null,所以可以定义一维数组赋值给二维数组。
第二种是二维数组已经定好了一维数组和大小,此时已经存在了一维数组地址一维数组已经真实存在了,但是一维数组的地址值被其他一维数组值覆盖了,这时候原来的一维数组地址就不再了。
总结
数组就是存放数据的一个容器叫做Array,可以先用new在堆区中申请一个数组对象,然后把数据放进去,这种就是静态初始化。
或者申请一个大小为x的数组对象,不把数据放进去,这时候系统会自动初始化,这种是动态初始化,根据申请的数组类型不同,系统默认的初始化值也会不同。
栈是方法运行时的时候才会有的。
二维数组就相当于一个箱子里放了两个数组(假如你定义了三个1一维数组那就是三个。):
- 特殊情况1:就是代表说明了这个箱子是存放两个数组的箱子,但是里面的箱子还没有放进来,也不知道那两个一维箱子有多大。
- 特殊情况2:就是说明大箱子里已经放了两个一维箱子进去了,然后觉得放的这两个一维箱子不合适,就重新换了两个箱子放进去,所以原本的箱子就只有从里面拿出来,不放在内存里自然就不见了。
-
- 至于说为什么装进去不拿出来,那就成了4个一维数组了,与定义描述的两个不符合。就像你买东西说你买的东西有两个组件组成,回家发现里面有四个组件组成,与卖家说的不一样这时候可以告他讹他一笔。
定义就是说一不二,说是什么就是什么,如果不是那就不是这个定义。
所以不需要在多想了。