在平常开发中,集合类是常常使用的工具,是JDK根据基础的java代码经过java语言来进行的一些封装。Java Collection由两套并行的接口组成,一套是Collection接口,一套是Map接口。Collection接口的子接口及实现类有List、Set、Stack、Queue等。java
List接口的特色是有序可重复。实现类一般使用的是ArrayList和LinkedList。数组
Set接口的特色是不可重复。实现类一般使用的是HashSet、LinkedHashSet和TreeSet。安全
Stack类的特色是后进先出,内部经过继承Vector来实现。并发
Queue接口的特色是先进先出,是中间件开发经常使用的类。函数
Map接口的特色是存储键值对,键不可重复。实现类一般使用的是HashMap、LinkedHashMap和TreeMap。工具
ArrayList底层由数组实现,数组的默认初始大小size为10,扩容系数为0.5。在构造函数中能够指定size。ArrayList特色是能够根据数组索引快速定位到元素。 扩容的源码:优化
int newCapacity = oldCapacity + (oldCapacity >> 1); // >>位移运算符,快速计算 /2的大小。 ... // 省略其余 elementData = Arrays.copyOf(elementData, newCapacity); // Arrays.copyOf()底层是native的数组复制,快速。
LinkedList底层由双向链表实现,特色是增长元素时能够快速添加。删除元素时,定位元素比较慢,定位到以后能够快速删除。线程
ArrayList和LinkedList是线程不安全的,推荐在方法内使用。Vector是线程安全版的ArrayList。设计
Set系列的实现是经过组合对应的Map实现类来实现的,Set存储的元素做为Map的key,value = new Object();code
HashMap底层由hash表+链表+数组实现的。JDK1.8以后HashMap的底层增长了红黑树结构。数组的默认初始大小size为16,扩容系数factor为2。在构造函数中能够指定size和factor。HashMap的特色是查询快速,是典型的以空间换时间的作法。缺点是扩容时很耗时。
LinkedHashMap是经过继承HashMap,重写entry内部类中增长双向链表来实现的。经过双向链表来表示元素的存取顺序。
TreeMap的底层是经过红黑树来实现的。
HashMap、LinkedHashMap和TreeMap是线程不安全的,推荐在方法内使用。HashTable是线程安全版的HashMap,可是不支持null做为key。
接下来简单介绍一下HashMap的原理。
put(k,v)时,先根据hashCode和equels判断是否已经存在相同的key,若是存在则返回旧的value并保存新的value。存储key时,先根据k.hashCode()获得hash值,再经过HahsMap本身实现的hash()等到更加均匀散列的hash值(JDK1.8去掉了此步骤),经过hash值对数组的长度求余来肯定该key应该存在的位置,其实就是获取hash值对应的数组索引。若是当前位置有值(说明发生了hash碰撞),则经过链表来增长该元素。最后再判断是否须要进行扩容resize()。
get(k)时,根据k.hashCode()计算获得数组索引,再遍历链表判断hashCode和equels肯定已存储的key来返回对应的value,若是没有则返回null。
根据hash值求余计算数组的索引,源码中采用的是位移运算 hash & (table.length -1)
,这也就是初始数组的大小会是2的n次方以及扩容系数是2的缘由。而在构造中保证数组大小是不小于传参的最小的2的n次方实现,也是经过位移运算(>>)来实现的。经过位移运算来保证运算速度,设计精巧。
HashMap的扩容resize()。
每一次put(k,v)元素以后,都须要进行判断是否进行扩容。当当前存储的元素的个数 size > table.length * factor
的时候,就须要进行扩容。扩容很耗时,须要创建新的数组,而后遍历就数组的每个元素并从新计算对应的数组索引并复制存储。因此使用HashMap的优雅方式是在构造参数中指定数组的大小(提早预估),即便是默认的16。这是优化HashMap的一个方法。HashMap在并发扩容的时候可能会发生死循环。
JDK1.8的HashMap中引入了红黑树结构,在put(k,v)的时候,当链表的节点数≥8,则自动转化为红黑树结构存储,目的是减小在极端状况下hash冲突致使都存储在同一个链表上时查询过慢的状况。由于链表查找的时间复杂度为O(n),而红黑树则是O(logn)。
HashMap容许存储key为null的状况。内部实现是经过将null做为特殊的key判断,存储在数组的第一个元素中。
ConcurrentModificationException
List、Set在进行foreach遍历的时候,是不容许经过list.remove(e)等操做来增删元素的,不然回报ConcurrentModificationException。foreach是经过iterator的.hasNext()和.next()来实现的。List有个modCount的属性来记录当前list被操做的次数,在add(e)、remove(e)、clear()等操做的时候都会触发modCount++。Iterator内部有expectedModCount属性,在构造Iterator的时候会expectedModCount = modCount。当使用iterator.remove()的时候,会调用list.remove(e)以及强制expectedModCount = modCount。每次iterator.next()的时候都会判断expectedModCount和modCount是否相等。若是不相等则表示被不正确的修改过,而后 throw new ConcurrentModificationException()
。若是要在遍历期间修改元素,请使用iterator.remove()或者listIterator.add(e)。