今天来介绍队列!
打算边学习,边把数据结构和一些常用的算法整理下,文章大概都会按照,是什么,为什么,怎么样来写,不算科普文章,更多是自己的一些思考和想法,还有找到一些优质资源的分享!
队列和栈一样,都是受限制的数据结构,记住一点:FIFO(first input first output),先进先出,就和排队一样(火车站买票,前面的人先买完离开)
所以队列提供的api,应该类似
enQueue/add/offer:往队列中添加数据的方法
deQueue/remove/poll:从队列头把数据移除
peek:不移除数据,但是可以查看队列头的数据
大家可以看看,java的Queue接口是如何定义的!
java Queue中 add/offer,element/peek,remove/poll区别(作者:行者小朱):https://blog.csdn.net/u012050154/article/details/60572567
其实我觉得为什么用队列也非常好理解,现实生活中,遇到排队的问题不要太多!各种买票,点餐,任何有顺序的事情,并且一次性处理不完,自然就要排队。
举个代码中的例子:
客户发起了创建自定义人群的任务,数据库一次性只能处理3个(线程池中只有3个线程),那么用户提交了10个怎么办,不就是创建一个队列,把所有任务按照先来后到的顺序放入到队列中,3个线程空闲,就直接到队列中拿头部的任务,其他的任务等待就好了,等执行的线程有空了,就会再次到队列中拿出排前面的任务去执行
所以如果java事先帮你实现了队列,那么你调用起来就非常的方便,新来一条数据,直接塞入队列,只需要每次都从队列里拿数据出来处理就可以了,不需要管队列里具体是什么(当然如果要写出性能好的代码,还是要研究下具体实现的嘿嘿~)
队列底层可以使用数组和链表来实现
(1)就是拿两个指针(front和rear),一个指向头节点,一个指向尾节点
(2)、插入数据,就往尾部插入,rear向后移动
(3)、移除数据,就是front往后移动一位,头节点没有被引用就会被GC自动回收掉
(1)、由于数组是固定长度的,所以数组实现的队列,就会有初始长度,而且当队列满了的时候,就要自动扩容(参照ArrayList这种底层是数组的集合的扩容实现)
(2)、还是要拿两个指针(front和rear),一个指向头节点,一个指向尾节点
(3)、但是问题来了,头节点的数据被移除,咋办,有两种选择,其一是每次移除,就把所有数据迭代一遍(往前移一位);其二,实现环形数组(至于环形数组咋搞,大家百度吧,就搜索环形数组实现队列,或者直接去看LeetCode上面的题目)
环形数组实现队列的难点就在于,如何判断队列空和满的情况!要么通过一个变量来记录现有的element个数,要么就需要多一个空间,以此用%运算来区分空和满(如果不知道我在说什么的,还是建议大家百度相关文章)
我不知道我能不能说全了,说一些常用的我见过的。
双向队列,可以看看java中的Deque接口的具体实现类,可以作为队列也可以作为栈来使用,还是挺厉害的!!
下图是我白嫖来的
Java双向队列Deque栈与队列(作者:森林屿麓):https://blog.csdn.net/u013967628/article/details/85210036
看最下面两个类,区别就是一个用链表实现,一个用数组实现!
有时候,我们不想按照数据进入的顺序来取数据,我们想定义优先级(这不就像是现实社会,你排队的时候,突然来了个VIP用户,然后他很自然就排到你前面去了,哭了TAT),比如一个优先级很高的任务,他将会被先处理!
大家可以看看这个类,PriorityQueue,他其实用数组实现了二叉树的结构,来完成排序的,大家有兴趣可以自行百度一些文章,也可以看我下面分享的这篇(但是可能需要你先理解二叉树这种数据结构,堆排序这一方面的知识)
JAVA中PRIORITYQUEUE详解(作者:Elliott_Su):https://www.cnblogs.com/Elliott-Su-Faith-change-our-life/p/7472265.html
我想把这两点放在一起讲
(1)、先说阻塞
如果你有去看queue的一些实现,你会发现,有些的名称上面有写blocking
阻塞队列使用于什么场景?
消息队列!(时至今日,消息队列应该也挺常见了吧,比如kafka,各种xxxMQ)
一个线程从队列中取数据的时候,如果队列为空,那么线程就会阻塞,等到队列有新的数据进来,线程才会取到数据,继续后续的操作,相对的,如果队列已满,线程插入数据的时候也会阻塞,等到有空间了再继续处理
(2)、线程安全
所以说到消息队列,一般消费者都会有多个吧,这就是多线程的情况,那我们就要考虑线程安全的问题!
可以看到基本所有的BlockingQueue都来自于java.util.concurrent(JUC)这个多线程包,你可以点到这些类里面具体看看,你会惊人的发现,他们基本上都使用了ReentrantLock这个类
这里就不具体介绍ReentrantLock类了,大家可以自行百度,这其实就是一个上锁的操作(synchronized关键字也是用来加锁的)
所以,阻塞队列使用了锁,那么他们就可以用到正式开发中的多线程环境中!!
emmm我发现LeetCode上有标签
1、访问:https://leetcode-cn.com/problemset/all/
2、右下角找到标签分类
3、进去做题就好了
我觉得:
首先,要能够自己实现环形队列
其次,知道队列底层是什么,能够发现工作中什么样的场景使用哪个(java已经定义好的)类性能更好(总不能啥轮子都自己造吧,写完还一堆bug就很糟糕)