数组
数组在内存中占据一段连续的内存,所有的数据在内存中连续排列。它的大小是固定的,这一特性使得数组对于插入操作并不友好,分析ArrayList
时就会看到这种操作的复杂。但数组对于位置的访问是极其友好的,它支持所谓RandomAccess
特性,这使得基于位置的操作可以迅速完成,其时间复杂度为O(1)。数组的数据顺序与插入顺序一致,所以查询操作需要遍历,其时间复杂度为O(n)。
所以数组最大的优势在于基于位置的访问,在扩展性方面表现无力。
链表
不同于数组,链表是通过指针域来表示数据与数据之间的位置关系的,所以链表在头部或尾部插入数据的复杂度仅为O(1)。链接不具备RandomAccess特性,所以无法提供基于位置的访问。其查询操作也必须从头到尾遍历,复杂度为O(n)。
散列表
散列表就是Hash Table,结合了数组和链表的优点,这种结构使用key-value形式存储数据,HashMap和HashTable就是基于它。
数组和链表在查询时表现一般的原因在于他们并不记得数据的位置,所以只能用待查询的数据和存储的数据依次对比。散列表使用一种巧妙的方式来减少甚至避免这种依次对比,它的原理是通过一个函数把任何的key转为int,每次查找时只需要执行这个函数便可以迅速定位。良好的设计使散列表的增、删、查等操作的时间复杂度均为O(1)。
二叉排序树
二叉排序树是解决查询问题的另一方案,如果数据在插入时是有序的,在查询时就可以使用二分法。二分法的时间复杂度为O(lg n)。
树的结构对二分法有天然的支持。二叉排序树牺牲了一部分插入的时间,但提高了查询的速度,同时有序的数据也可以做些其他操作。如果查询的操作重要性超过了插入,就应该考虑这种结构。二叉排序树也存在一些不平衡导致效率下降,所以有了AVL树,红黑树,以及用于数据库索引的B树,B+树等。