来吧!给你不一样的数组深入讲解
不知道为啥,数组让我写的很痛苦!写到深夜两点啊,不容易,各位看官,记得点赞????,不然没动力了
数组小谈????
庆哥: 嗨,小白,知道啥是数组吗?????小白: 你看你这话说的,数组那还不简单,学计算机的没有不知道数组的吧,我们刚开始接触C语言的时候就有数组啊,现在在学习java,也有数组啊,一般不就这样嘛????
int[] array = new int[10]
这就创建了一个长度为10的数组,是不是?????
庆哥: 嗯嗯,你还知道数组的啥啊。比如特性啥的?
小白: 这个嘛,数组啊,最经典的不就是可以根据数组下标来读取数据吗?比如上面定义的那个长度为10的数组,可以使用array[1]来获得数组第二个位置的数据。
庆哥: 为啥不是第一个的数据,那不是1吗?
小白: 这个嘛,那是因为数组下标是从0开始的,所以实际上1就是2的位置,就像这样:
庆哥: 那数组下标为啥从0开始啊????
小白: 嗯。。。这个?????不知道
庆哥: 在Java中数组其实可以看做是一个对象嘛?????
小白: 纳尼?没有考虑过这个问题啊????
庆哥: 知道Array和Arrays嘛????
小白: 不是太清楚????
庆哥: 哈哈,来吧,今天就让你彻底搞懂数组这家伙????
小白: 小板凳已备好????
啥是数组啊????
天马星空看数组????
庆哥: 既然我们要学习数组,那首要的任务就是先要弄懂,啥是数组?你现在清空大脑,不要想着什么数组是数据结构,什么连续内存,什么随机访问,什么不可变的,抛出你之前关于数组的所有认知,从零开始,你想想,数组是个啥?????
小白: 那我要发挥我超强的想象力了哦????,数组嘛,单从这俩字啊,我想着这玩意应该是个跟棍似的????,一组一组的嘛,长长的????,然后是数据嘛,人家是数组,那应该就是跟数据有关的,组?我们平常会说一组一组的,比如几人一组,那这个数组就是一些数据在一块。。。。我在说啥????
庆哥: 可以啊,你这想象力????,那我来说下我的理解吧,初看数组这货,我就觉得啊,它在java中应该也是个类,然后我们也可以通过最基本的new来创建一个数组,刚开始就这样感觉得,但是谁知道这家伙完全不按照套路出牌啊????
小白: 你这样一说也真是的,我本来也以为数组可以使用new嘞,有的时候还是有点疑惑的,特别是遇到Array这个类就更迷惑了????
庆哥: 的确,我刚开始也是这样,觉得这个Array不就是数组嘛,然后我还这样试过????
哈哈,我当时还迷惑,这是咋回事啊,这不是创建数组嘛????
小白: 这是咋回事啊,为啥会报错啊????
庆哥: 这个嘛,我们看看Array的源码就知道了看到了吧,它的构造方法是私有的,所以是不能被实例化的,因为我们使用new就是通过构造方法去创建啊,而这里是私有的,所以是不行滴,知道了吧????
小白: 嗯嗯,看来我对这个数组还真的是不了解啊????,庆哥这次要好好和我说说????
java中的数组
庆哥: 没问题????,那我们言归正传,看看java中的数组到底是个啥,在java中啊,我们可以这样理解数组:
数组这家伙就是一个数据集合,而且它还是有序的,就是很多数据搁在一块,一个挨着一个,而且每个数据的类型都是相同的
这里简单大白话说了啥是数组,有必要还是画个图,来加深下印象,对了对了,之前画了个图,就用这个
好了,现在对于数组,给你文字说了是啥,现在再看个图,这里就相当于有一个数组,这个数组有10个数据,你看他们是不是一个挨着一个,然后每个数据还有一个下标,这个下标就相当于给每个数据进行编号,你想啊,编号有啥好处?
是不是更加容易寻找了,对号入座嘛!所以这里就引出了数组的一个大大的特点:
人家支持随机访问啊
小白: 这个是不是就是可以使用下标访问数据的特性????
庆哥: 嗯嗯,是的,我们接着来说,我们现在对数组有个大致印象, ,要知道数组最终是用来干嘛的。人家是用来存放数据的,我们还是先来看看数组的的相关代码吧:
int[] a = new int[10];
看,这就创建了一个数组,不是我们想的使用new的方式,其实从代码我们就能猜到,这个数组的长度是10,啥意思,数组人家是有长度的。
小白: 这个是必须要给它指定一个长度吧
数组的声明
庆哥: 这个就牵涉到数组的声明了,数组是用来存放数据的,那我们要用它的话就要先创造它吧,之前说了,人家不是使用new,人家是这样的形式
int[] a = new int[10];
那么这样可以吗?
int[] a = new int[];
有啥区别?是不是少了个10,行吗这样,我们看看
看到了,不行,为啥不行啊,你想想?
小白: 这个嘛,这里的10是在给数组一个指定的长度吧,难道不能默认长度为0吗?
庆哥: 这里就要看数组的一些特点了,对于数组啊,人家是有序数据的集合,在内存中来展示就是人家需要连续的内存空间,还有一个特点那就是你一旦声明就不会再改变了
小白: 嗯嗯,这个我知道,数组一旦确定将是固定不变的,哦哦,我知道了,那这里就必须要求指定长度了,不然相当于没创建数组啊,长度为0没意义啊????
数组在内存中的分配
庆哥: 对的,我们再看这段代码
int[] a = new int[10];
是个怎么回事,首先我们知道了,这是要创建数组吧,长度也指定了,是10,对了这里忘了说了,这个数组是个整型数组,类型是int,为啥要说这个嘞,等会再详细说,先来看图
啥意思嘞?这些个格格就可以看成是内存,一块块的内存,现在我们创建一个长度为10的整型数组,那就需要十块这样的内存空间。
小白: 这里红色的块是啥意思啊?????
庆哥: 红色啊,就代表已经被使用了的内存空间,看出啥问题了没,现在你创建这样的代码
int[] a = new int[10];
意思就是你告诉内存,“内存那家伙,给我来十块内存空间,记住必须连续的哦????”,也就是说,数组申请的内存空间是连续分配的,数组中的数据比较矫情,必须一个挨着一个????
简单聊聊初始化
小白: 嗯嗯,我记得数组的声明不止这一种吧?????
庆哥: 是的,我们之前介绍的这样叫做动态初始化数组,啥意思嘞,就是指定了长度,但是没有给值,我们看这个
int[] a = new int[10];
System.out.println(a[2]);
输出是0,你可以试下,这10个数据都是0,这是默认初始化的值,我们也可以使用这样静态初始化
int[] b = new int[]{1, 2, 3};
啥意思,也就是在创建数组的时候就把值给确定下来,你猜猜这样创建的数组长度是多少?
小白: 这个啊,这里没有给定像10那样的数据啊????,这个应该是。。。3吧????,后面大括号有三个数
庆哥: 我们看下这段代码
int[] b = new int[]{1, 2, 3};
System.out.println(b.length);
这是啥,这是求数组长度的,我们看看输出
被你说对了,像这样的,它会根据你大括号里的数据创建内存空间,也就是说有几个数就申请几个内存,不多不少申请了就固定了
数组变量和数组对象
庆哥: 我们上面已经简单介绍完数组了,那你知道什么是数组变量什么是数组对象吗????
小白: 我去,晕菜,这俩双胞胎吧????
庆哥: 那我来说说,啥是数组变量,看之前的代码
int[] a = new int[10]
这个a其实就是一个数组变量,一个数组变量指向一个数组对象,数组变量其实就是一个引用,存放的是内存地址,也就是引用是与内存地址划等号的,内存地址是给计算机看的,而引用是给我们看的(一个我们比较熟悉的符号),而这个a其实是数组的首地址,指向的数组对象其实就是a[0]这个数组变量对应的数组对象。
那数组对象其实就是内存空间中存放的数值了。
数组初始化是对谁初始化
那我们接着这个数组变量和数组对象来讲讲,数组初始化是对谁进行初始化,其实也很简单,知道了什么是数组变量和数组对象之后,我们就应该知道,数组初始化不是对数组变量初始化,而是对数组对象进行初始化。
所谓的对数组对象初始化,无非就是告诉内存,我要创建一个数组,你得给我分配一块连续的内存。
咋样,明白了吧????
小白: ok的????
数组能存储啥????
庆哥: 好了,以上属于数组的基本知识,不多说了,我们接下来再来说说数组的数据存储类型,还记得之前说的吗?数组中的每个元素都是相同的类型,我们上面是int类型的,啥意思嘞?也就是说啊,你创建什么类型的数组就只能存放什么类型的数据。
比如这样就不行
因为你创建了是个int整型的,所以只能存放整型数据,字符串就不行,想要存放字符串的话那就要创建一个字符串数组
String[] strings = new String[]{"hello"};
有个小例外
这里啊,是有个例外的 ,我们看这个:
Object[] objects = new Object[10];
objects[0] = 1;
objects[1] = "hello";
这个是都可以的,知道为啥不????
小白: 这个啊,知道,Object可是java中所有类的超类啊????
庆哥: 正解,好了,咱来简单总结下:
Java中的数组是用来存储同一种数据类型的数据结构,一旦初始化完成,在内存中的空间就已经固定了下来
数组是个对象?
庆哥: 你说数组是不是个对象?????
小白: 数组是对象?不是说数组不能用new的方式创建吗,那应该不是吧,感觉和平常的对象不一样啊????
庆哥: 实话告诉你吧,数组啊,其实是个特殊的对象????,只不过数组比较特殊,它不同于一般的对象,像一般的对象都有特定的java类,比如我这里创建了一个Person类,然后有这个代码:
Person person = new Person();
System.out.println(person.getClass().getName());
就是创建一个Person对象,然后得到它的类名,如下
再来看个
Object object = new Object();
System.out.println(object.getClass().getName());
这个知道吧,它的类名一定是Object,我们看
是吧,我们再来看数组的,它可以这样获得
//数组是特殊的对象,没有对应的类文件,数组类是在运行时产生的,类名很奇怪
System.out.println(new int[2].getClass().getName());
它这个类是啥嘞
在这里插入图片描述
这是啥玩意????
小白: 我去,这里我是不是只要记住,其实数组也是个特殊的对象就行了吧????、
庆哥: 对的,只需要记住,数组其实也是个对象就行,这可是对数组的深层次理解哦????,那接下来我们继续说数组的两个让人懵圈的类。
Array和Arrays
对这俩货熟悉吗????
小白: 很懵圈啊,之前我还想使用Array来创建数组嘞????
庆哥: 哈哈,使用它创建数组是不行的,话说这俩货是干嘛的啊,太容易让人迷惑了,实际上啊,这俩货的存在都是为了让我们更一步的操作数组的,先来个简单的例子,比如我们创建一个数组
int[] b = new int[]{1, 2, 3};
System.out.println(Array.get(b, 1));
看到没,我们平常是使用b[1]来获取下标为1的元素,使用Array就可以像上面那样获取,也就是说Array给我们提供了可以直接操作数组的一些方法,有如下这些:小白: 这就是个类似工具类啊,可以方便我们操作数组是吧????
庆哥: 对的,还记得我们之前就说过这个Array吗,它的构造方法是私有的,不能实例化,它的方法也都是静态的,可以直接还用类名来调用,就像上面那样,就是为了我们更加方便的操作数组而存在的。
小白: 这样说的话,那这个Arrays是不是类似啊????
庆哥: 是的,Arrays也是个方便我们操作数组的工具类,只是他们提供的功能有所不同,我们使用Arrays可以实现给数组填充数据,也就是赋值,也可以进行排序啊,进行二分查找啊,截取数组啊之类的????
小白: 这个提供的功能还不少嘞????,使用上是不是和Array类似啊
庆哥: 是的,你看,比如说给数组数据进行排序
System.out.println("排序:");
int[] array1 = new int[]{5, 7, 8, 9, 1, 3, 6};
Arrays.sort(array1);
看它提供的方法
在这里插入图片描述
也有很多,需要啥我们用啥就行了
咋样,这样一介绍,是不是发现,其实这俩货也没啥难的啊????、
小白: 是啊,原来就是两个打辅助的啊????
数组的特点
庆哥: 到了这里,你是不是能 总结下数组的一些简单特点啦????
小白: 嗯嗯,数组啊,连续内存分配,可以随机访问,这次刚刚学到的,数组其实是个对象????,对了,数组还有下标,是从0开始的。
庆哥: 嗯嗯,问题来了,为啥要从0开始,你知道吗?
下标索引为啥从0开始
小白: 这个还真不知道,平常就记着下标是从0开始的了,至于为啥是从0开始还真不知道嘞????,为啥啊庆哥。
庆哥: 要弄明白这个问题,要看这个图
我们已经知道了,数组的创建需要连续的内存空间,比如这里的整型数组,长度为10,那就再内存开辟了相应的内存空间,在内存空间中,每个内存是有相应的地址的,这个是操作系统干的事,当你申请了内存之后,操作系统会为这块内存地址进行编号。
比如这里申请了一个长度为10的整型数组,我们知道int在java中占4个字节,也就是说一个数据占四个字节的内存,也就是上面的一块.
然后每一块都有相应的内存地址编号,比如0对应的这块,编号是1000-1003,占四个字节嘛,我们接下来结合数组的随机访问,比如我们使用array[0]去访问第一个元素,实际指向的就是1000这个内存地址,这个在数组中也叫作首地址,数组变量array就是指向这个首地址。
这个首地址也叫作base_address,记住这个,然后我们如果要访问下标为1的怎么访问嘞,这里有个寻址公式
array[i] = base_address + i*data_type_size
base_address我们知道是啥了,那data_type_size是啥?其实即使数据类型的字节长度,比如这里的int整型就是4个字节,那data_type_size就是4,我们现在要访问下标为1的数据,代入公式就是:
array[1] = 1000 + 1*4 = 1004
看下,是不是刚好定位到下标为1的那块内存的位置。
你想想,如果索引从1开始,那寻址供视是不是就变成了
array[i] = base_address + (i-1)*data_type_size
这就要多一步操作,浪费性能啊????
咋样明白不????
小白: 嗯嗯,原来是这么回事,终于学会了????
数组的增删改查
庆哥: 嗯嗯,那我们再来看看数组的增删改查,也就是对数组进行的一些基本操作,首首先看看数组增加,我们还看着这个图来说
如果我们要增加一个元素的话,怎么办?
小白: 这分几种情况吧,插在最前面,中间和最后。
庆哥: 如果是插在中间呢?发现啥问题没?????
小白: 嗯。。我想想。。数组内存是连续分配的,那要插入一个新的元素,那岂不是要把当前位置的元素以及后面的元素全部后移了,这样才能腾出位置插入当前元素吧????
庆哥: 对的,所以对于数组,不仅是插入,删除也是同样的道理,如果不是尾部操作,都需要进行数组挪动操作。
小白: 那这个就费劲了????
庆哥: 是啊,所以数组啊,插入删除效率并不高,最坏的情况,时间复杂度是O(n),好的情况那就是尾部操作,那就是O(1)了,这个知道吧????
小白: 嗯嗯,知道,那查找效率是不是高啊
庆哥: 这里我觉得大家都有个误区,什么嘞,数组如果是使用下标访问,那自然效率高,可是我们平常查找的话是根据数值也不都是下标啊,这样就需要遍历数组找到我们想要的,这个要明白
小白: 是啊,你这样一说,我倒知道了
数组扩容
庆哥: 对了,想一下,如果数组满了咋办????
小白: 这个啊,数组满了,就无法插入新的元素,这样的话就需要对数组扩容,这个扩容,我想想,数组一旦初始化完成,就是固定不变得,这样的话,是不是需要新创建一个数组啊????
庆哥: 对的,数组一旦初始化完成,在内存中的空间就已经固定了下来,即使某个元素被清空,但其所占的空间依然是保留的,因此数组长度是不能被改变的,要想改变数组的空间就必须进行数组扩容。
小白: 那这个是不是就需要把原数组的数据全部复制过去啊
庆哥: 对的,有如下简单代码小白: 哦哦,明白了????
完!!!
ps:数组这块怎么说嘞,反正写的有点痛苦????,现在已经是夜里两点了,先到这吧,如有问题,欢迎大家留言讨论!
感谢阅读 更多高质量编程视频www.shangyepingtai.xin
推荐阅读