当新建一个栈时,不推荐写成:
Stack<Integer> stack=new Stack<>()
而是:
Deque<Integer> stack=new ArrayDeque<>()
就来说说Java 语言中的 Stack 类,有什么问题?
Java中的Stack类继承了Vector这个类。Vector是一个动态数组
这样Stack就继承了Vector的所有公有方法
Vector作为动态数组,有能力在数组中的任何位置添加或删除元素,Stack也有了这样的能力
stack.add(1,666);
对于栈来说,指定在index为1这个位置插入666这个元素,破坏了栈这种数据结构的封装
用户可能会有意无意地调用这些操作,这就将成为bug的来源
出现这样的问题,原因是:
Stack和Vector之间的关系不应该是继承关系,而应该是组合关系(composition)
继承
:is-a 是一个,例如:猫是一个动物,猫类继承了动物类
组合
:has-a 有一个,例如:车里有一台发动机,发动机类和车类是组合关系(车中有发动机类的对象这个成员变量)
在真实世界中,真正的继承关系很少,组合关系更常用(多用组合,少用继承)
Java官方不改Stack类的原因:
若修改,使用老版本Java的程序将在新的Java环境下无法执行
Deque接口:
Deque其实是双端队列的意思,可以在两端进行插入和删除操作,然后真正的栈只能在同一端做插入和删除操作。
就如前面所说,Stack类的问题是继承了Vector这个类的若干不需要的方法,破坏了封装性。而Deque接口的stack依然有不需要的方法,这是Java的历史遗留问题,现在已经无解了。
总结:虽然 Java 官方推荐使用 Deque 接口实现 stack,但是这样的 stack 也破坏了封装性,并不安全。
LinkedList与ArrayDeque的区别:
使用ArrayDeque动态数组,如果触发了扩容操作,世界复杂度为O(n)
使用LinkedList链表,不会牵扯到扩容问题,因此每一个添加操作,时间复杂夫欧式O(1)
可实际上,当数据量达到一定程度的时候,链表的性能是远低于动态数组的,因为链表每添加一个元素都要重新创建一个Node类对象,也就是进行一次new的内存操作,而对内存的操作是非常慢的
总结:在实践中,尤其是面对大规模数据的时候,不应该使用链表!
对于ArrayList,如果你的应用场景不需要线程安全的特性,那么对于动态数组,应该使用 ArrayList
Stack与ArrayDeque方法的不同:
Stack:
stack.push()
stack.pop()
stack.peek()
ArrayDeque:
stack.addLast()
stack.removeLast()
stack.peekLast()