常用数据结构及其Java实现

以下是Java的基本接口/类层次结构:

java.util.Collection [I]
    +--java.util.List [I]
       +--java.util.ArrayList [C]    
       +--java.util.LinkedList [C]  
       +--java.util.Vector [C]    //线程安全
          +--java.util.Stack [C]  //线程安全
    +--java.util.Set [I]                   
       +--java.util.HashSet [C]      
       +--java.util.SortedSet [I]   
          +--java.util.TreeSet [C]  
    +--Java.util.Queue[I]
       +--java.util.Deque[I] //允许两头进出,称为双端队列(Double Ended Queue)
	  +--java.util.ArrayDeque[C]
	  +--java.util.LinkedList[C]
       +--java.util.PriorityQueue[C]  
java.util.Map [I]
    +--java.util.SortedMap [I]
       +--java.util.TreeMap [C]
    +--java.util.Hashtable [C]   //线程安全
    +--java.util.HashMap [C]
    +--java.util.LinkedHashMap [C]
    +--java.util.WeakHashMap [C]

[I]:接口
[C]:类
补充:LinkedList既实现了List接口,又实现了Queue接口,在使用时,建议用特定的接口来引用它, 方便判断其用途。

顺序表:

顺序表是指用一组地址连续的存储单元依次存储数据元素的线性结构。


这里稍微补充下,在网上看到很多都是把数组和其他数据结构并列在一起说,个人感觉这样容易让人混乱。实际上,这是两种不同的逻辑角度。数组是具体的实现方式,线性表等是抽象概念,比如数组既可以实现顺序表也可以用来实现一些非线性结构如树。


其优点是:get和set操作时间上都是O(1)的;缺点是:add和remove操作时间上都是O(N)的。

Java中,可用Array实现静态顺序表,ArrayList实现动态顺序表(实际上ArrayList也是使用了Array作为其实现基础)。Vector和ArrayList相比,主要差别就在于多了一个线程安全性,也因此效率比较低下。

链表:

链式存储的线性表,简称链表。链表由一系列节点(node)通过指针连接起来,每个节点按功能可分两部分:数据域和指针域,分别用来存数据和指向其他单元。
链表的第一个和最后一个节点分别为头节点和尾节点。
链表数据结构主要包含单向链表、双向链表和循环链表。

在Java中,我们考察List<E>接口,可以看到几个主要的接口方法:

  • 在末尾添加一个元素:void add(E e)
  • 在指定索引添加一个元素:void add(int index, E e)
  • 删除指定索引的元素:int remove(int index)
  • 删除某个元素:int remove(Object e)
  • 获取指定索引的元素:E get(int index)
  • 获取链表大小(包含元素的个数):int size()

ArrayList通过数组实现List接口,另一种LinkedList则通过“链表”实现了List接口。

LinkedList linkedList = new LinkedList<>();
linkedList.add("addd");//add
linkedList.set(0,"s");//set,必须先保证 linkedList中已经有第0个元素
String s =  linkedList.get(0);//get
linkedList.contains("s");//查找
linkedList.remove("s");//删除

栈和队列:

队列(Queue)是只允许在一端进行插入,而在另一端进行删除的线性表,从队尾(Rear)进从队头(Front)出,FIFO。

在Java的标准库中,队列接口Queue和Deque定义了以下的几个方法:

图片来自廖雪峰的Java教程

除了以上方法外还有: int size():获取队列长度;
对于具体的实现类,有的Queue有最大队列长度限制,有的Queue没有。注意到添加、删除和获取队列元素总是有两个方法,这是因为在添加或获取元素失败时,这两个方法的行为是不同的。上面每个单元格内左边失败会throw Exception,右边返回false或null。

栈(Stack):只允许在一端进行插入或删除操作的线性表,栈底(Bottom)固定栈顶(Top)进出,LIFO。

Java中,Stack实现了这种特性(底层也是数组),但是Stack也继承了Vector,所以具有线程安全线和效率低下两个特性,最新的JDK8中,推荐用Deque来实现栈:

  • 把元素压栈:push(E);
  • 把栈顶的元素“弹出”:pop(E);
  • 取栈顶元素但不弹出:peek(E)
Deque stack = new ArrayDeque();
stack.push(12);//尾部入栈
stack.push(16);//尾部入栈
int tail = stack.pop();//尾部出栈,并删除该元素
tail = stack.peek();//尾部出栈,不删除该元素

参考资料:
https://www.liaoxuefeng.com/wiki/1252599548343744/1255943629175808
http://mageek.cn/archives/26/
https://www.jianshu.com/p/8e54797ec3e0 (自己手写)
https://www.cnblogs.com/xdecode/p/9321848.html

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注