Collection
List:
- 可以允许重复对象,可以插入多个null元素,是一个有序容器
- 其常用子类有ArrayList、LinkedList、Vector
ArrayList、LinkedList和Vector的区别
- ArrayLis和Vector底层都是基于数组实现的,随机访问效率较高,插入效率低
- ArrayLis线程不安全,不可以设置空间扩容方法。
- Vector线程安全,可以设置空间扩容方法
- LinkList底层是基于双端链表,线程不安全,随机访问效率较低,随机插入效率高。
Set:
- 不允许重复对象
- 其常用子类有HashSet、LinkedHashSet、TreeSet
Hashset
- 底层数据结构是哈希表,HashSet不保证元素的顺序但保证元素必须唯一。
LinkedHashSet
- 底层数据结构是哈希表+链表,哈希表用来保证元素唯一,链表用来保证元素的插入顺序.
TreeSet
- 底层数据结构是红黑树,TreeSet不仅保证元素的唯一性,也保证元素的顺序。
- 一般用于存储有序的集合(自然排序和比较器排序)
异同点:
- 都保证了元素的唯一性,线程都不安全
- HashSet不保证元素的顺序
- LinkHashSet保证FIFO即按插入顺序排序
- TreeSet保证元素的顺序,支持自定义排序规则
- HashSet,LinkedHashSet允许添加null值,TreeSet不允许添加null值。
Map:
- Map不是Collection的子接口或实现类。Map是一个接口
- Map 的每个Entry都特有两个对象,也就是一个键一个值,Map可能会持有相同的值对象但键对象必须是唯一的,Map里可以拥有随意个null值但最多只能有一个null键.
- 其常用子类有:HashMap,TreeMap,HashTable,LinkedHashMap,ConcurrentHashMap.
hashMap
- 底层基于哈希表实现的,每个元素都是一个key–value对,对内部通过单链表解决冲突问题,容量不足时会自动增长,实现了Serializable接口,它支持序列化,实现了Cloneable接口,能被克隆。
- HashMap 默认的初始化大小为16。负载因子是0.75,当容量达到容量的0.75倍时开始扩容,容量变为原来的两倍。
- 链表长度大于8时,结构由链表转为红黑树
- HashMap不同步,线程不安全。
HashTable
- hashtable继承Dictionary类。
- hashtable中的key value都不允许出现null值
- 线程安全
- HashTable扩容后是原来的2倍+1
TreeMap
- TreeMap是利用红黑树来实现的,实现了SortMap接口,能够对保存的记录根据键进行排序。所以一般需要排序的情况下是选择TreeMap来进行。
- 继承于AbstractMap,所以它是一个Map,即一个key-value集合。
- 实现了NavigableMap接口,意味着它支持一系列的导航方法。比如返回有序的key集合。
ConcurrentHashMap
- 线程安全的HashMap
- 采用了CAS算法+同步锁,保证线程安全
LinkedHashMap
- 有序的HashMap
- 线程不安全
- 根据插入/访问顺序排序
HashMap和Hashtable的区别
- HashMap 允许 key 和 value 为 null,Hashtable 不允许。
- HashMap 的默认初始容量为 16,Hashtable 为 11。
- HashMap 的扩容为原来的 2 倍,Hashtable 的扩容为原来的 2 倍加 1。
- HashMap 是非线程安全的,Hashtable是线程安全的。
- HashMap 的 hash 值重新计算过(jdk1.8 hash(key)),Hashtable 直接使用 hashCode。
- HashMap 去掉了 Hashtable 中的 contains 方法。
- HashMap 继承自 AbstractMap 类,Hashtable 继承自 Dictionary 类。
HashTable和ConCurrentHashMap的区别
- ConcurrentHashMap是线程安全的HashMap的实现。
- HashTable里使用的是synchronized关键字,这其实是对对象加锁,锁住的都是对象整体,当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。
- ConcurrentHashMap(jdk1.7)使用了分割,将一个map分成多个小的hashtable,对map的一部分进行上锁。保证同步的同时,有提高了性能。具体的put操作:先判断是否需要扩容,扩容整理后根据key.hashcode()找到对应的Segmentm,再往Segment中put键值对,这个时候put是加锁的,利用自旋锁去尝试获取锁,获取锁后判断key是否存在,存在就覆盖不存在就添加一个键值对。总之就是利用再入锁的方式锁住Segment,保证只有一个线程在操作Segment,这就相当于在HashMap中保证了只有一个线程在数组的一个位置中put,这就不会形成环形链表了。
- ConcurrentHashMap(jdk1.8):将每个segment的分段锁ReentrantLock改为CAS+Synchronized。
参考: