ArrayDeque

Python013

ArrayDeque,第1张

写cs61b题目时惊叹为什么会有这种东西,于是搜索了一番,发现这容器还是很有意思的,于是搬运了一下。

参考: ArrayDeque - (jianshu.com)

Java ArrayDeque - Java教程 - 菜鸟教程 (cainiaojc.com)

在ArrayDeque类实现这两个接口:Java Queue和Java Deque

使用了数组来存储数据,同时用两个int值 head 和 tail 来表示头部和尾部。不过需要注意的是 tail 并不是尾部元素的索引,而是尾部元素的 下一位 ,即下一个将要被加入的元素的索引。

ArrayDeque 有三个构造函数来初始化,除了无参的构造函数使用了默认容量,其它两个构造函数会通过 allocateElements 函数来计算初始容量

数组的大小很特殊,大小必为2的n次方(2^n)。下面我们看看 allocateElements 方法

(1)对于一个小于2^30的值,经过五次右移和位或操作后,可以得到一个 2^k - 1 的值。最后再将这个值 +1 ,得到 2^k 。通过这个方法,可以将一个任意的初始值转化为2^n的值.

(2)不过有一点不足在于,如果本身传进来的值就是 2^n 的值,那么经过转化会变成 2^(n+1) ,所以我们在不用刻意去传入 2^n 的值。

(3)如果传入的值大于等于 2^30 ,那么经过转化会变成负值,即<0,此时会把初始值设置为 2^30 ,即最大的容量只有 2^30

在ArrayDeque中,数组是作为环形来使用的,正常情况下在末尾添加元素后,tail=tail+1是要判断是否越界,如果越界,会变为从索引0开始。参考如下图片,当H添加到索引7后,tail值会+1,此时tail=8,但是越界了,所以应该将tail设置为0。

我们看看 tail = (tail + 1) &(elements.length - 1) 的正确性:

所以当 tail+1 <= length - 1 ,此时数组并没有越界, (tail + 1) &(elements.length - 1) 后得到的还是 tail+1 。如果 tail + 1 = length ,此时数组越界了, (tail + 1) &(elements.length - 1) 后得到0。

所以通过 (tail + 1) &(elements.length - 1) 可以跳过条件判断在环形数组中获取正确的索引值,然后再判断新的 tail 是否等于 head ,如果结果为 true ,那么数组已经满了,需要扩容,即 doubleCapacity() 。

原理和addLast相同

无论是从头部还是从尾部添加元素,都会判断 tail==head ,如果两个索引相遇,说明数组空间已满,需要扩容操作.

ArrayDeque支持从头尾两端移除元素

在上文中,我们讲了怎么去实现一个ArrayDeque,但Java标准库中已经为我们准备好了,我们只需按规则使用就行

add() - 将指定的元素插入ArrayDeque双端队列 末尾

addFirst() -在ArrayDeque双端队列的 开头 ,插入指定的元素

addLast() - 在ArrayDeque双端队列的 末尾 插入指定的内容(等效于 add() )

注意:如果ArrayDeque双端队列已满,则所有这些方法 add() , addFirst() 和 addLast() 都会引发IllegalStateException

offer() - 将指定的元素插入ArrayDeque双端队列的 末尾

offerFirst() - 在ArrayDeque双端队列的 开始处 插入指定的元素

offerLast() - 将指定的元素插入ArrayDeque双端队列的 末尾

注意: offer(),offerFirst()并offerLast()返回true是否成功插入元素;否则,返回。如果ArrayDeque双端队列已满,则这些方法返回false。

getFirst() - 返回ArrayDeque双端队列的 第一个元素

getLast() - 返回ArrayDeque双端队列的 最后一个元素

注:如果ArrayDeque双端队列为空,getFirst()和getLast()抛出NoSuchElementException。

peek() - 返回ArrayDeque双端队列的 第一个 元素

peekFirst() - 返回ArrayDeque双端队列的 第一个 元素(等效于 peek() )

peekLast() - 返回ArrayDeque双端队列的 最后一个 元素

注:如果ArrayDeque双端队列为空,peek(),peekFirst()和getLast()抛出 NoSuchElementException

remove() - 返回并从ArrayDeque双端队列的 第一个 元素中删除一个元素

remove(element) - 返回并从ArrayDeque双端队列的 头部 删除指定的元素

removeFirst() - 返回并从ArrayDeque双端队列中删除 第一个 元素(等效于remove())

removeLast() - 返回并从ArrayDeque双端队列中删除 最后一个元素

注意:如果数组双端队列为空,则remove(),removeFirst()和removeLast()方法将引发异常。 另外,如果找不到元素,则remove(element)会引发异常。

poll() - 返回并删除ArrayDeque双端队列的 第一个 元素

pollFirst() - 返回并删除ArrayDeque双端队列的 第一个 元素(等效于poll())

pollLast() - 返回并删除ArrayDeque双端队列的 最后一个 元素

注意:如果ArrayDeque双端队列为空,则如果找不到该元素,则poll(),pollFirst()和pollLast()返回null。

删除所有元素

iterator() - 返回可用于遍历ArrayDeque双端队列的 迭代器

descendingIterator() -返回一个迭代器,该迭代器可用于以 相反顺序 遍历ArrayDeque双端队列

注:为了使用这些方法,我们必须导入java.util.Iterator包。

使用迭代器的方法如下

element() -从ArrayDeque双端队列的头部返回一个元素。

contains(element) -在ArrayDeque双端队列中搜索指定的元素。如果找到该元素,则返回true,否则返回false。

size() -返回ArrayDeque双端队列的长度。

toArray() -将ArrayDeque双端队列转换为数组并返回。

clone() -创建ArrayDeque双端队列的副本并返回它。

push() - 在堆栈顶部添加一个元素

peek() - 从堆栈顶部返回一个元素

pop() - 返回并从堆栈顶部删除元素

这里直接把s赋值给readContent是有问题的,问题点如下:

假设文件有两行

第一行

第二行

则读取循环两次,第一次s赋值给readContent,readContent的值为 "第一行",然后又循环,读取第二行,再次赋值,则readContent就会变成 "第二行",第一行的值就丢失了;

这里StringBuff的作用是把读取到的内容连接起来成为一个字符串;

不过这里的例子的确有问题,问题却不在StringBuffer上,而在 s 变量上,s中间两步是不必要的,循环内直接改成如下形式:

删除s.getBytes()和new String

另外,除了以上问题,来总结下这段代码的问题吧:

1、文件资源没有释放

2、在不涉及多线程的情况下,应使用StringBuilder类进行字符串拼接,而不是StringBuffer,因为StringBuilder更快(但是StringBuilder线程不安全,StringBuffer线程安全);

3、异常处理有问题,如果出现IO异常,就返回异常信息的字符串?那么如何区分这个字符串是发生了异常还是从文件中读取到的内容呢?这里不应该返回字符串,而是直接声明异常,有异常就向外抛,直到能处理的顶层程序处理。

基于以上几点,修改代码如下:

注意第27行,声明本方法可能抛出 IOException 异常,稍后主程序将进行处理。另外注意第30行,创建InputStreamReader 的时候指定了文件编码为utf8,以避免乱码,你的文件如果存储的是其他编码类型,则会出现乱码。

调用代码如下:

getContent函数调用主程序

注意18行和21行,是分别处理正常情况和异常情况,当读取异常时,将会调用第21行,打印异常信息;

整体代码如下:

全部代码

接下来我们看看代码执行情况。

1、当我们要读的文件不存在时,执行结果如下:

我们要读的文件不存在时,抛出异常

看,当文件不存在时,抛出异常,并由异常处理程序捕获和打印,你可以看到具体哪一行代码出错[getContent(App.java:29)],说明在App.java 文件中的getContent方法,文件的29行出错。

2、那么正常情况下呢?当然是输出文件内容了,我们放一个有如下内容的文件在"D:\\file.txt"处:

要读取的文件内容

执行结果如下:

正常情况下,输出文件内容

最后,我不知道你看的是什么书,是不是课本,但感觉质量不高,这里给你推荐几本,可以买来看看:

1、Head First java

2、菜鸟教程 Java

3、最权威的,自然是The Java® Language Specificati了

祝你学习之路顺利