目录
[4.1 接口的实现](#4.1 接口的实现)
[5, ArrayList简介](#5, ArrayList简介)
[6.1 ArrayList的构造方法](#6.1 ArrayList的构造方法)
[6.2 ArrayList的常见操作](#6.2 ArrayList的常见操作)
[6.3 ArrayList的遍历](#6.3 ArrayList的遍历)
[7.1 简单的洗牌算法](#7.1 简单的洗牌算法)
[7.2 杨辉三角](#7.2 杨辉三角)
[7.3 面试题](#7.3 面试题)
1,什么是List
在集合框架中,List是一个接口,继承自Collection。
Collection也是一个接口,继承自Iterable。
他的一些方法:
Iterable是最顶层的一个接口
站在数据结构的角度来看,List就是一个线性表,即n个具有相同类型元素的有限序列,在该序列上可以执行增删改查以及变量等操作。
List中提供了好的方法,具体如下:(部分)
2,List的使用
注意:List是个接口,并不能直接用来实例化。
如果要使用,必须去实例化List的实现类。在集合框架中,ArrayList和LinkedList都实现了List接口。
3,线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
4,顺序表
顺序表其实就是一个数组,数组本身是一块连续的内存
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
4.1 接口的实现
对数组进行增删改查:
定义一个IList接口:
定义MyArrayList类实现IList接口:
定义一个测试类Test:
接下来就对其接口里面的功能在重写方法里面一一完成:
1,新增元素,默认在数组最后新增
确定新增位置,只需要放到usedSize位置,usedSize+1即可。但是数据结构是一门非常严谨的学科,在这里我们还要判断数组是否已满!!!即在接口里面定义一个方法判断数组是否已满!
重写这个方法,若usedSize等于数组长度,则满
2,在pos位置新增元素
重最后一个数据往后面挪,才能插入指定位置,所以定义一个下标i,i的起始位置是usedSize-1的位置,把i位置的值赋值给i+1,在i--,i<pos结束,i>=pos都要移动,再将需要插入的元素存入pos位置,usedSize++即可!!!但是要考虑pos位置是否合法,不能<0,且不能跳起存放,数据结构规定当你在插入一个数据或删除一个数据是,当前数据前面一定要有唯一的前驱(连续存放),即不能pos>usedSize,若满了扩容。
在MyArrayList类中写一个方法判断pos合不合法:
若不合法,可抛出一个异常,则定义一个异常类:
最终代码:
3,判定是否包含某个元素(给一个数,查找数组中是否包含这个数)
查找次数不用把整个数组查找完,只需要查找usedSize次即可
4,查找某个元素对应的位置
5,获取pos位置的元素
在这种情况下,pos不能等于usedSize,pos合法的位置只要0~usedSize-1,其他都不合法!
即写一个方法来判断pos位置元素是否合法:
6,给pos位置的元素设为value--->相当于更新
首先检查pos位置的合法性,不能pos<0,不能pos>=usedSize,即可直接调用checkPosofGet方法,可将此方法名修改一下,以便观察与理解
7,删除第一次出现的关键字key
可定义一个下标i,代表你要删的位置,让i+1的值赋值给i下标的值,然后i++,再让i+1的值赋值给前面的值,走到i<usedSize-1的位置即可,最后在usedSize--。要判断你要删除的关键字是否在这个数组里面
8,获取顺序表的长度
9,清空顺序表
这个数组是一个基本数据类型,如果要清空它,直接将usedSize置为0即可,因为在我们插入的时候,是按照usedSize位置放的
但是如果数组当中的元素是引用类型该怎么办?
这个数组里面的元素不是基本数据类型,它是一个一个的对象,即数组里面存的是对象的地址,如果说只是将usedSize置为0,引用类型势必会引用一个对象,但是里面的元素还在引用着之前的对象,导致不需要的对象还在引用它,这叫做内存泄漏。正确的做法是遍历数组,把每个元素置为null,再把usedSize置为0。
10,打印顺序表
至此,这就是我们自己实现的一个顺序表 使用于经常进行下标查找或更新的操作
ArrayList自己也有以上方法
5, ArrayList简介
在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:
这里发生向上转型,动态绑定,ArratList重写了List的add方法,即list调用ArrayList的add方法
6,ArrayList的使用
6.1 ArrayList的构造方法
1,不带参数的构造方法
我们说顺序表的背后就是一个数组,那么可进入到elementDate的源码
此时这个数组还没有初始化,默认为空,只是一个数组引用,但发现这个不带参数的构造方法里面,elementDate等于DEFAUL... 进入他的源码
发现这个数组也没有分配内存,长度为0,也就是说调用不带参数的构造方法,并没有给数组分配大小,那么数组没有分配大小,为什么还能add呢
进入到add的源码
发现里面还有一个add方法,在进入此时add的源码
发现当我们add(10)的时候,if语句成立elementDate等于了grow方法,grow方法是一个扩容方法啊,进入grow方法的源码
继续进入这个grow方法
查看黄色框框的源码
当if语句不成立时,会给elementDate分配一个内存,在1和10取最大值。
也就意味着,当调用不带参数的构造方法进行add的时候,第一次add会分配大小为10的内存
当if条件成立时,会进行1.5倍扩容
2,带参数的构造方法
进入带参数的构造方法里面,通过if语句判断,6大于0成立,就会给这个数组elementDate分配一个长度为6的数组。如果给的是0,elementDate这个数组就会等于EMPTY_...,我们进入到EMPTY_...的源码 在else给一个小于0的数,就会抛一个异常
发现他也是一个空的数组
相当于这个带参数的构造方法是在初始化数组指定大小
3,比较奇怪的构造方法
Collection是顶层的一个接口,可实例化ArrayList,只要实现了该接口都可以进行传递,?代表通配符,E代表通配符的上界,相当于尖括号里面的类型要么是E,要么是E的子类
例如:
就是说arrayList这个引用他的类型相当于是ArrayList<Integer>,然后ArrayList这个类型也实现了Collection这个接口,<Integer>1是<Integer>2的本身或子类,所以arrayList就可以传递
6.2 ArrayList的常见操作
addAll()
remove()
我们在删除的时候,可以传一个数组下标,也可以传一个对象
如果要删一个对象
subList()
源码:
当我们对list进行一更新
在打印arrayList
当add完以后,list截取了arrayList的[ 1,3 )下标的元素,这个截取并不是生成了一个新的list,而是让list直接引用了1下标元素的地址,根本没有重新生成一个新的对象,即修改list下标的元素,也会影响arrayList所对应下标的元素。
6.3 ArrayList的遍历
ArrayList 可以使用三方方式遍历:for循环+下标、foreach、使用迭代器
for循环:
for each
迭代器:
iterator源码:
是一个接口
所以的打印都借助 it 引用进行打印
判断 it 是否有下一个,有进来,打印下一个元素,打印的同时 it.next()也会向后走一步
还可以这样:
Iterator不可进行传参,默认重0下标开始遍历
查看listIterator的源码
也并不独特,是继承于Iterator
还可以倒着遍历
7,ArrayList的具体使用
7.1 简单的洗牌算法
1,生成52张牌(没有大小王)
定一个类,用来完整的表示一张牌,在定义成员变量(牌由数组和花色组成)
在定义一个类,用来表示所生成的52张牌
2,洗牌
在Cards类里面写一个方法,进行洗牌:
洗牌逻辑:下标与下标之间进行交换,定义下标 i 从最后一个51开始,生成[ 0,i )的随机数,与 i 下标的元素进行交换,换完之后 i-- ,再把 i 传给随机数的生成,继续交换
3,三个人轮流接五张牌
问题一:给个人抓的牌放到哪里?
给每个人申请一个list,只要抓到一张牌就往list里面放
问题二:三个人轮流抓5次牌?
给两个for循环
问题三:怎么抓牌?
所有的牌都在cardList里面,每次抓的牌都是第一张,只需要remove第一张牌即可
问题四:抓到牌你怎么知道放到哪个人的list里面
代码:
4,测试类
生成的牌:
洗的牌:
抓牌及剩余的牌:
还可以自己增加点功能:
比如对拿到的牌进行排序,可通过Collections,这个Collections是来操作集合的工具类,里面有个sort方法,可进行对list进行排序
7.2 杨辉三角
每一行的一个是1,每一行的最后一个也是1
代码:
7.3 面试题
st1:welcome to bit
st2:come
要求:删除第一个字符串当中所有的第二个字符串当中的字符。
例如该例中返回结果:wl t bit
方法一:顺序表
只需要遍历st1,拿到某个字符,判断这个字符是否存在st2当中?
若不存在,放到List当中即可
代码:
方法二:StringBuilder
判断之后使用啊append进行拼接即可
8,ArrayList的优缺点
优点:适合根据下标进行查找和更新的场景
缺点:
- ArrayList底层使用连续的空间,任意位置插入或删除元素时,要移动元素,最坏情况下,0下标插入或删除时,要移动所以元素,故时间复杂度为O(N)。
2.扩容空间可能会浪费,假设现在100个长度放满了,还有一个元素没放,此时需要1.5倍扩容,多了50个,但实际只存放了一个元素。
- 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗
为了弥补这些缺点,就有了链表!!!