比较经常使用的集合通常有4种接口,每种接口能够由多种数据结构去实现。这样咱们就能够在实现相同接口(功能)的状况下,根据具体场景选择不一样的数据结构。java
Interface | Hash Table | Resizable Array | Balanced Tree | Linked List | Hash Table + Linked List |
---|---|---|---|---|---|
Set | HashSet | TreeSet | LinkedHashSet | ||
List | ArrayList | LinkedList | |||
Deque | ArrayDeque | LinkedList | |||
Map | HashMap | TreeMap | LinkedHashMap |
ArrayList、LinkedList、Vector都实现了接口List。List接口是一个有顺序的集合,元素能够重复,能够经过索引去访问元素。api
ArrayList类维护了一个数组,访问操做效率高,删除和插入操做效率低。数组
transient Object[] elementData; // non-private to simplify nested class access
当增长元素且ArrayList的容量不够时,须要对数组进行扩容,扩容使用Arrays.copyOf()
方法。
当删除元素时,会使用System.arraycopy()
方法。
以上两个copy方法都是蛮费时间的。缓存
LinkedList类维护了一个双端链表,first和last分别指向链表的头尾Node。安全
Vector类与ArrayList类很是相像,主要的区别是:Vector是线程安全的,ArratList不是线程安全的。数据结构
protected Object[] elementData;
Stack类继承了Vector类,由于Vector类维护了一个数组,在数组基础上实现LIFO的功能仍是很方便的。但这也会带来2个问题:oracle
setElementAt(E obj, int index)
,那么Stack就不是纯粹的栈,栈只能操做栈顶的元素。JDK文档建议,若是要使用栈这种数据结构,应该优先使用Deque<Integer> stack = new ArrayDeque<Integer>();
优化
Deque是Double End Queue
的意思,该双端队列便可以用做FIFO的队列,也能够用做LIFO的栈。Stack类也是栈,但它有些缺点,应该优先使用Deque做为栈。线程
LinkedList类维护了一个双端链表,first和last分别指向链表的头尾Node。code
ArrayDeque是在JDK1.6中才出现的。以数组的形式实现双端队列。
JDK文档建议:
第1点的缘由在上面说过解释过,第2点的缘由有如下两点:
HashMap(散列表)是一个增删、查找都高效的数据结构。查找散列表通常分为2个步骤:
java中的HashMap采用的是拉链法,这是数组和链表的结合形式。
HashMap中的put()方法:
hash%length
,length是数组的长度。该长度应该是2的幂方,这样咱们就能够对取模操做进行优化hash&(length-1)
静态内部类Entry
中。Entry保存键、值以及指向下一个键值对的next。HashMap中的resize()方法:
当entry的数量大于阈值时,开始resize()。其中阈值是容量乘以加载因子。
resize()以后,table长度变为2倍,从新取模,从新链表插入。
负载因子:当hashmap中的有效容量除以总容量大于负载因子时,hashmap的容量就会翻倍。java的hashmap默认容量是16,负载因子时0.75。
容量:
Java8对HashMap的改进:当链表的长度大于必定值时,链表就转换为红黑树,实现了快速的增删改查,时间复杂度从顺序查找的O(n)优化到O(logn)。
HashTable与HashMap很是类似,他们有如下三点区别:
【Reference】