HashSet:元素不可重复,无序,底层是通过在内部建一个HashMap的实例对象,几乎全部调用HashMap的方法来实现。
TreeSet:元素不可重复,有序,底层是通过在内部建一个TreeMap的实例对象,几乎全部调用TreeMap的方法来实现。
所以下面重点分析HashMap和TreeMap来解释:
HashMap:初始容量为16,加载因子0.75,可以存null,异步式线程不安全,键不可重复,值可重复,键值对重复就覆盖。
本质上是基于可变数组和链表实现的(jdk1.8之后如果链表长度超过8,链表就转变为红黑树,以后长度小于6时再转变为链表),初始容量为16,加载因子为0.75即长度为16的数组,每当超过0.75的总容量时扩容,每个元素称为桶,桶内部存方了一个链表用来存储键值对。
底层源码中键值对Entry的hash值,是根据键K和值Value的hash值通过^(抑或位运算)求得,所以当Value值为空时Entry的hash值就是K的hash值,可以被HashSet(可以认为是没有Value的键值对映射)直接借用。Entry的hash值确定存放桶的位置,如果桶内有元素,就通过equals方法分别比较,如果相同就覆盖原有元素,如果不同则放入链表的第一个位置。一旦扩容所有键值对元素都必须重新hash再存入新的HashMap中,频繁扩容开销比较大,应当尽量避免,在构造函数中指定好合适的初始容量。
综合以上叙述可以看到,HashMap结合了可变数组及链表的优势,查询定位及增删效率都比较高。
TreeMap:是通过红黑树实现的,所以不存在初始容量及加载因子的概念,键值对元素通过实现Comparable接口实现compareTo方法比较来确定顺序,当元素没有实现此接口时,需要程序员指定外比较器Comparator重写compare方法来比较得出顺序,红黑树是一个自平衡的二叉树(最末尾子节点层数差不超过1),每当有元素增删时,通过恢复红黑树第四条和第五条原则实现调整树结构(此处较为复杂建议查看https://blog.csdn.net/mbmispig/article/details/78750405,写的是我查阅资料里面最为全面的)
HashMap扩展:Hashtable、ConcurrentHashMap、LinkedHashMap、WeakHashMap
Hashtable:线程安全,初始容量13,加载因子0.75,所有方法都被Syncharonized关键字锁住,每当操作数据时,将整个表锁住以此保证数据安全。
ConcurrentHashMap:初始条件同Hashtable,异步式线程安全,操作数据时,通过锁住当前数据所在的桶,其他桶仍然可以操作,以此保证数据安全,较为推荐。
LinkedHashMap:在HashMap基础上增加一个链表记录元素增删查询记录,以此来记录元素的顺序,在记录重要配置的操作日志时可以用到。
WeakHashMap:当使用HashMap处理海量数据时,频繁的扩容消耗系统性能,无用内存始终占用导致内存大量浪费,由于HashMap是强引用,内部元素不被引用也不会被清除,而WeakHashMap中的元素是若引用,每次gc收集垃圾时标记未被引用的元素,维护在一个待处理队列表中,当我们再次操作WeakHashMap时,就会比对待处理队列当中的元素,如果没有处理,那么待处理队列中的元素都将被清除。数据处理效率极高!