集合框架知识的梳理

1. Iterator接口

Iterator接口,这是一个用于遍历集合中元素的接口,主要包含hashNext(),next(),remove()三种方法。它的一个子接口LinkedIterator在它的基础上又添加了三种方法,分别是add(),previous(),hasPrevious()。也就是说若是是先Iterator接口,那么在遍历集合中元素的时候,只能日后遍历,被遍历后的元素不会在遍历到,一般无序集合实现的都是这个接口,好比HashSet,HashMap;而那些元素有序的集合,实现的通常都是LinkedIterator接口,实现这个接口的集合能够双向遍历,既能够经过next()访问下一个元素,又能够经过previous()访问前一个元素,好比ArrayList。java

2. List

List是元素有序而且能够重复的集合。
List的主要实现:ArrayList, LinkedList, Vector。算法

2. ArrayList、LinkedList、Vector 的区别

  ArrayList LinkedList Vector
底层实现 数组 双向循环链表 数组
同步性及效率 不一样步,非线程安全,效率高 不一样步,非线程安全,效率高 同步,线程安全,效率低
特色 查询快,增删慢 查询慢,增删快 查询快,增删慢
默认容量  10 / 10
扩容机制 int newCapacity = oldCapacity + (oldCapacity >> 1); //1.5 倍 / 2 倍

总结数组

  • ArrayList 和 Vector 基于数组实现,对于随机访问get和set,ArrayList优于LinkedList,由于LinkedList要移动指针。
  • LinkedList 不会出现扩容的问题,因此比较适合随机位置增、删。可是其基于链表实现,因此在定位时须要线性扫描,效率比较低。
  • 当操做是在一列数据的后面添加数据而不是在前面或中间,而且须要随机地访问其中的元素时,使用ArrayList会提供比较好的性能;
  • 当你的操做是在一列数据的前面或中间添加或删除数据,而且按照顺序访问其中的元素时,就应该使用LinkedList了。

3. Set

Set集合中的对象不按特定的方式排序(存入和取出的顺序不必定一致),而且没有重复对象。
Set的主要实现类:HashSet, TreeSet。安全

 

  HashSet TreeSet LinkedHashSet
底层实现 HashMap 红黑树 LinkedHashMap
重复性 不容许重复 不容许重复 不容许重复
有/无序 无序 有序,支持两种排序方式,天然排序和定制排序,其中天然排序为默认的排序方式。 有序,以元素插入的顺序来维护集合的连接表
时间复杂度 add(),remove(),contains()方法的时间复杂度是O(1) add(),remove(),contains()方法的时间复杂度是O(logn) LinkedHashSet在迭代访问Set中的所有元素时,性能比HashSet好,可是插入时性能稍微逊色于HashSet,时间复杂度是 O(1)。
同步性 不一样步,线程不安全 不一样步,线程不安全 不一样步,线程不安全
null值 容许null值 不支持null值,会抛出 java.lang.NullPointerException 异常。由于TreeSet应用 compareTo() 方法于各个元素来比较他们,当比较null值时会抛出 NullPointerException异常。 容许null值
比较 equals() compareTo() equals()

 

HashSet如何检查重复

当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其余加入的对象的hashcode值做比较,若是没有相符的hashcode,HashSet会假设对象没有重复出现。可是若是发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。若是二者相同,HashSet就不会让加入操做成功。
hashCode()与equals()的相关规定:数据结构

  • 若是两个对象相等,则hashcode必定也是相同的
  • 两个对象相等,对两个equals方法返回true
  • 两个对象有相同的hashcode值,它们也不必定是相等的
  • 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖
    hashCode()的默认行为是对堆上的对象产生独特值。若是没有重写hashCode(),则该class的两个对象不管如何都不会相等(即便这两个对象指向相同的数据)。

总结:
HashSet是一个通用功能的Set,而LinkedHashSet 提供元素插入顺序保证,TreeSet是一个SortedSet实现,由Comparator 或者 Comparable指定的元素顺序存储元素。多线程

4. Map

Map 是一种把键对象和值对象映射的集合,它的每个元素都包含一对键对象和值对象。 Map没有继承于Collection接口从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
Map 的经常使用实现类:HashMap、TreeMap、HashTable、LinkedHashMap、ConcurrentHashMap并发

  HashMap HashTable  
底层实现 数组+链表 数组+链表  
同步性 线程不一样步 同步  
null值 容许 key 和 Vale 是 null,可是只容许一个 key 为 null,且这个元素存放在哈希表 0 角标位置 不容许key、value 是 null  
hash 使用hash(Object key)扰动函数对 key 的 hashCode 进行扰动后做为 hash 值 直接使用 key 的 hashCode() 返回值做为 hash 值  
容量 容量为 2^4 且容量必定是 2^n 默认容量是11,不必定是 2^n  
扩容 两倍,且哈希桶的下标使用 &运算代替了取模 2倍+1,取哈希桶下标是直接用模运算  

几个问题:

1. HashMap 的工做原理?
经过hash的方法,经过put和get存储和获取对象。存储对象时,咱们将K/V传给put方法时,它调用hashCode计算hash从而获得bucket位置,进一步存储,HashMap会根据当前bucket的占用状况自动调整容量(超过Load Facotr则resize为原来的2倍)。获取对象时,咱们将K传给get,它调用hashCode计算hash从而获得bucket位置,并进一步调用equals()方法肯定键值对。若是发生碰撞的时候,Hashmap经过链表将产生碰撞冲突的元素组织起来,在Java 8中,若是一个bucket中碰撞冲突的元素超过某个限制(默认是8),则使用红黑树来替换链表,从而提升效率。
2.get和put的原理吗?equals()和hashCode()的都有什么做用?
经过对key的hashCode()进行hashing,并计算下标( n-1 & hash),从而得到buckets的位置。若是产生碰撞,则利用key.equals()方法去链表或树中去查找对应的节点
3. HashMap 的长度为何是2的幂次方?
为了能让 HashMap 存取高效,尽可能较少碰撞,也就是要尽可能把数据分配均匀,每一个链表/红黑树长度大体相同。这个实现就是把数据存到哪一个链表/红黑树中的算法。框架

HashMap 和 LinkedHashMap 的区别

  • LinkedHashMap 拥有与 HashMap 相同的底层哈希表结构,即数组 + 单链表 + 红黑树,也拥有相同的扩容机制。
  • LinkedHashMap 相比 HashMap 的拉链式存储结构,内部额外经过 Entry 维护了一个双向链表。
  • HashMap 元素的遍历顺序不必定与元素的插入顺序相同,而 LinkedHashMap 则经过遍历双向链表来获取元素,因此遍历顺序在必定条件下等于插入顺序。
  • LinkedHashMap 能够经过构造参数 accessOrder 来指定双向链表是否在元素被访问后改变其在双向链表中的位置。

HashMap & TreeMap 的区别

HashMap实现了Map接口,不保障元素顺序。
TreeMap实现了SortedMap接口,是一个有序的Map。内部采用红黑树实现,红黑树是一种维护有序数据的高效数据结构函数

ConcurrentHashMap 和 Hashtable 的区别

ConcurrentHashMap 和 Hashtable 的区别主要体如今实现线程安全的方式上不一样。
底层数据结构: JDK1.7的 ConcurrentHashMap 底层采用 分段的数组+链表 实现,JDK1.8 采用的数据结构跟HashMap1.8的结构同样,数组+链表/红黑二叉树。Hashtable 和 JDK1.8 以前的 HashMap 的底层数据结构相似都是采用 数组+链表 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的;
实现线程安全的方式(重要): ① 在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不一样数据段的数据,就不会存在锁竞争,提升并发访问率。(默认分配16个Segment,比Hashtable效率提升16倍。) 到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操做。(JDK1.6之后 对 synchronized锁作了不少优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,可是已经简化了属性,只是为了兼容旧版本;② Hashtable(同一把锁) :使用 synchronized 来保证线程安全,效率很是低下。当一个线程访问同步方法时,其余线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另外一个线程不能使用 put 添加元素,也不能使用 get,竞争会愈来愈激烈效率越低。
高并发

HashMap的底层实现

JDK1.8 以前 HashMap 由 数组+链表 组成的(“链表散列” 即数组和链表的结合体),数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的(HashMap 采用 “拉链法也就是链地址法” 解决冲突),若是定位到的数组位置不含链表(当前 entry 的 next 指向 null ),那么对于查找,添加等操做很快,仅需一次寻址便可;若是定位到的数组包含链表,对于添加操做,其时间复杂度依然为 O(1),由于最新的 Entry 会插入链表头部,急须要简单改变引用链便可,而对于查找操做来说,此时就须要遍历链表,而后经过 key 对象的 equals 方法逐一比对查找.所谓 “拉链法” 就是将链表和数组相结合。也就是说建立一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中便可。

相比于以前的版本, JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减小搜索时间。

TreeMap、TreeSet以及JDK1.8以后的HashMap底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,由于二叉查找树在某些状况下会退化成一个线性结构。

ConcurrentHashMap线程安全的具体实现方式/底层具体实现

在jdk1.8以前,首先将数据分为一段一段的存储,而后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其余段的数据也能被其余线程访问。

ConcurrentHashMap 是由 Segment 数组结构和 HahEntry 数组结构组成

Segment 实现了 ReentrantLock,因此 Segment 是一种可重入锁,扮演锁的角色。HashEntry 用于存储键值对数据。

static class Segment<K,V> extends ReentrantLock implements Serializable { } 复制代码

一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和HashMap相似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每一个 HashEntry 是一个链表结构的元素,每一个 Segment 守护着一个HashEntry数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先得到对应的 Segment的锁。

在jdk1.8时,ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构相似,数组+链表/红黑二叉树。

synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提高N倍。

集合框架底层数据结构总结

Collection

1. List

  • Arraylist: Object数组
  • Vector: Object数组
  • LinkedList: 双向循环链表

2. Set

  • HashSet(无序,惟一): 基于 HashMap 实现的,底层采用 HashMap 来保存元素
  • LinkedHashSet: LinkedHashSet 继承与 HashSet,而且其内部是经过 LinkedHashMap 来实现的。有点相似于咱们以前说的LinkedHashMap 其内部是基于 Hashmap 实现同样,不过仍是有一点点区别的。
  • TreeSet(有序,惟一): 红黑树(自平衡的排序二叉树。)

Map

  • HashMap: JDK1.8以前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减小搜索时间
  • LinkedHashMap: LinkedHashMap 继承自 HashMap,因此它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增长了一条双向链表,使得上面的结构能够保持键值对的插入顺序。同时经过对链表进行相应的操做,实现了访问顺序相关逻辑。详细能够查看:《LinkedHashMap 源码详细分析(JDK1.8)》
  • HashTable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的
  • TreeMap: 红黑树(自平衡的排序二叉树)
相关文章
相关标签/搜索