Java 堆栈ADT(抽象数据类型)实现-数组与链接

Java 堆栈ADT(抽象数据类型)实现-数组与链接,java,arrays,collections,linked-list,stack,Java,Arrays,Collections,Linked List,Stack,基于数组和链接实现堆栈的优缺点是什么。根据我有限的知识,我觉得linked始终是实现Stack的更好方法,因为: 1) 不需要随机访问 2) 阵列效率低下,因为它们必须调整大小(浪费时间),而且它们对存储的使用效率低下(总是浪费一些空间) 我确信我在这里遗漏了一些东西,因为: 1) java.util.Stack是基于array实现的(它是java.util.Vector的一个子类,它是java集合接口创建之前的遗留类,实际上与ArrayList类似)。因此,java的创建者选择了基于数组的实现

基于数组和链接实现堆栈的优缺点是什么。根据我有限的知识,我觉得linked始终是实现Stack的更好方法,因为:

1) 不需要随机访问

2) 阵列效率低下,因为它们必须调整大小(浪费时间),而且它们对存储的使用效率低下(总是浪费一些空间)

我确信我在这里遗漏了一些东西,因为:

1) java.util.Stack是基于array实现的(它是java.util.Vector的一个子类,它是java集合接口创建之前的遗留类,实际上与ArrayList类似)。因此,java的创建者选择了基于数组的实现

2) 我读过一篇关于stackoverflow的回答,“另一方面,基于数组的实现可能具有更好的运行时行为”。虽然我不知道这是什么意思

我要查找的比较应包括以下参数:

1) 理论时间和存储要求

2) 运行时性能(如果与理论比较不同)

请包括我因缺乏知识而未能提及的任何其他重要参数。如果这对结论有任何影响的话,我会使用java

另外,我无法在本网站的任何其他答案中找到此问题的所有要点,因此请仅将此问题标记为重复问题,以防我的所有问题都在其他问题中得到正确和足够详细的回答


p.p.S-我知道这是一个很长的问题,所以对于你的努力来说:)如果你觉得它太宽了,那么请在你标记它为“太宽”之前评论一下如何分解它,以便我可以根据需要编辑它。

首先,你应该知道
java.util.Stack
是一个可以追溯到java 1.0的“遗留集合”。它扩展了真正基于数组的
java.util.Vector
。然而,这通常被认为是糟糕的对象设计。这并不意味着基于阵列的堆栈是一件坏事,但是您应该知道,仅仅因为JDK中有一些东西并不意味着它是一个好主意。对于较旧的API尤其如此

更现代的类似堆栈的数据结构是
java.util.ArrayDeque
。它也是基于阵列的。它还有很多其他特性,但是如果你坚持使用它的
push
pop
方法(相当于
addFirst
removeFirst
),它基本上就是一个堆栈。请注意,在其文档中指出

当用作堆栈时,此类可能比
堆栈
更快

如果您看一下实现,
堆栈
,就像
向量
,是同步的,这样可以使它慢一点
Stack
push
pop
方法是根据
Vector
方法实现的,这些方法也是同步的。这意味着额外的方法调用加上嵌套同步。(不过,JIT可能会优化掉其中的大部分。)相比之下,
ArrayDeque
是不同步的,它的堆栈式方法使用直接在内部数组上操作的简单操作。注意,我在这里没有做任何基准测试来验证文档的声明

在任何情况下,
ArrayDeque
都是解决需要类似堆栈行为的问题的首选Java集合

但您询问的是链接数据结构,而不是基于数组的数据结构。让我们将
ArrayDeque
与另一个Java链接数据结构
LinkedList
进行比较。这实现了
Deque
,因此它也可以用作堆栈。你说

1) 不需要随机访问

对。请注意,
ArrayDeque
不提供随机访问,即使它是基于阵列的。对任何一方都没有好处

2) 阵列效率低下,因为它们必须调整大小(浪费时间),而且它们对存储的使用效率低下(总是浪费一些空间)

任何数据结构都会有一些低效。不过,不同的数据结构会有不同的权衡。如果
ArrayDeque
的数组大小不适合堆栈的典型容量,则必须调整其大小。但一旦阵列足够大,就不需要不断调整大小。如果堆栈收缩,数组仍将消耗空的空间。这可能被视为浪费,也可能被视为保留内存,以便在堆栈再次增长时不必调整大小和复制

将情况与
LinkedList
进行比较。在内部,每个列表元素都需要一个
节点
实例。(参见源代码)每个实例包含三个引用:一个引用到数据元素,一个引用到下一个
节点
,一个引用到上一个
节点
。假设使用压缩指针的64位JVM,即每个引用32位。每个对象都有一个包含64位标记字和32位类指针的标头。这将提供总共六个32位字,或每个列表元素24字节。六个字中只有一个是“有效负载”——元素本身——因此每个元素的开销为20字节或83%

相反,数组中的每个附加元素只消耗引用该元素的空间,即32位

例如,在
LinkedList
中存储1000个元素会消耗大约24K的内存,但在
ArrayDeque
中存储它们只会消耗大约4K的内存。即使数组的大小是容纳1000个元素所需大小的两倍,也只有8K——仍然只是
LinkedList
大小的一小部分

“另一方面,基于数组的实现可能具有更好的运行时行为”

这可能是指遍历链表比遍历数组慢。这有两个原因。首先,链路节点占用更多内存,因此必须使用更多内存