程序 = 算法 + 数据结构
操作数据的增、删、改、查、排序及相关的逻辑运算都要基于数据结构
下面简单罗列下常用的一些数据结构
数据结构简单分类
1. 数组(Array,ArrayList)
创建数组必须明确指定数组的长度,并且需要分配一段连续的存储空间
数组既然是定长的,就会遇到需要动态扩容的情况,通常通过复制原数组实现:
Arrays.copyOf(elementData, size, Object[].class);
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
集合中的ArrayList就是基于数组特性实现的,因此是有序的,数据元素允许重复。
ArrayList 随机读取元素很方便,但是在操作加减元素时,重新排序索引比较浪费性能。
2.链表(LinkedList)
链表分单链表、双链表、环链表
链表
链表是由指针控制数据节点与节点之间的关联关系,由于存在指针存储区,因此数据存储密度要低于ArrayList, 并且空间存储上是不连续的,因此是不能像ArrayList那样通过集合下标随机获取元素,即LinkedList存储的元素是无序的,数据元素不允许重复。
ArrayList 与 LinkedList使用场景取舍:
频繁读取修改元素,宜用ArrayList; 频繁加减元素,宜用LinkedList
3.队列(Queue)
先进先出(FIFO),从队列尾部添加,从头部查询或删除
Queue 继承于Collection类,因此有集合通用的add(), remove()方法
常用实现类:
(1)ConcurrentLinkedQueue 线程安全的队列
offer(); // 加入元素
peek(); // 获取队列头部第一个元素
poll(); // 获取队列头部第一个元素,并从队列中移除
下面说下阻塞队列:
(2)ArrayBlockingQueue 有界队列
基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象,其内部没实现读写分离,也就意味着生产和消费不能完全并行,长度是需要定义的,可以指定先进先出或者先进后出,也叫有界队列,在使用该队列时必须指定队列的容量大小
// 超时未添加成功返回false, 该方法优于add()方法
boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException;
(3)LinkedBlockingQueue 无界队列
可以不设置队列的长度,若指定了长度则同有界队列
(4)SynchronousQueue 不能存储元素,即时处理
(5)PriorityBlockingQueue
基于优先级的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定,也就是说传入队列的对象必须实现Comparable接口),在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁,他也是一个无界的队列。
(6)DelayQueue
带有延迟时间的Queue,其中的元素只有当其指定的延迟时间到了,才能够从队列中获取该元素。DelayQueue中的元素必须实现Delayed接口,DelayQueue是一个没有大小限制的队列,应用场景很多,比如对缓存超时的数据进行移除、任务超时处理、空闲连接的关闭等等。
队列应用场景
4.双端队列(Deque)
可以在队列的首尾两端操作添加,删除,查询。
举一个应用场景,比如我们可以控制双端队列一端生产,一端消费
5.栈(Stack)
限制队列只能在一端操作,就成了栈,特点是后进先出(Last Input First Output)
栈-LIFO
6.树
树,最基本的就是二叉树,由此可以演变出平衡二叉树,满二叉树,完全二叉树,斜树,
红黑树(通过旋转及变换,追求局部平衡),
BTree(多阶树,节点存储 索引+数据,不满足单一原则,稳定性下降),
B树
B+树
哈夫曼树(最优树,就是指带权路径最小的树,仅包含度为0或者2的节点)
森林(一棵树为树,两棵树及以上则称为森林)
树一般设计的算法思想就是 递归
树的遍历
(1) 前序遍历(Preorder Traversal) 输出:ABDHIEJCFG
方法:先访问根节点,然后访问左子树,最后访问右子树。在访问左、右子树的时候,同样,先访问子树的根节点,再访问子树根节点的左子树和右子树,这是一个不断递归的过程。
应用场景:运用最多的场合包括在树里进行搜索以及创建一棵新的树。
(2)中序遍历(Inorder Traversal)输出:HDIBJEAFCG
方法:先访问左子树,然后访问根节点,最后访问右子树,在访问左、右子树的时候,同样,先访问子树的左边,再访问子树的根节点,最后再访问子树的右边。
应用场景:最常见的是二叉搜索树,由于二叉搜索树的性质就是左孩子小于根节点,根节点小于右孩子,对二叉搜索树进行中序遍历的时候,被访问到的节点大小是按顺序进行的。
(3) 后序遍历(Postorder Traversal)输出:HIDJEBFGCA
方法:先访问左子树,然后访问右子树,最后访问根节点。
应用场景:在对某个节点进行分析的时候,需要来自左子树和右子树的信息。收集信息的操作是从树的底部不断地往上进行。