HashMap
定义
HashMap是继承了Map接口的类。
因此它具备map的特点,它的元素同map一样是<K,V>结构的键值对,并且在存储方式上采用了哈希桶的结构,它将K值作为关键码,通过hash函数,将元素存储到对应的位置。
以上为HashMap源码。(以下源码,均来自IDEA)
可见,HashMap实现了了Map接口,克隆接口,Serializable接口,这与TreeMap没有什么区别,但我们要知道,TreeMap强制了K必须是可比较的,但HashMap并没有这个规定,原因也很简单,TreeMap底层是红黑树,必须根据K的值,来进行排序,但是HashMap只需要将K代入到哈希函数,求出对应的值,实现元素存储,因此,不需要是可比较的对象。从这也可以看出HashMap中的元素,在TreeMap中是有序的,但是在HashMap中是不一定有序的,并且HashMap的Key可以为NULL。
原理
那么,HashMap底层实现数据存储的原理是什么呢?
在HashMap类下有两个静态内部类,Node与TreeNode,Node是一个作为单链表结点的类,TreeNode则是一颗红黑树。
开散列法
又称链地址法,是将K哈希后的值作为关键码,将相同关键码放在同一个集合中,集合中的元素通过一个单链表链接,头结点存储在哈希表中。
下图,为HashMap静态内部类Node的定义(部分)。
HashMap还有一个Node[]的成员变量。
如下图:
可见HashMap以Node来进行存储的时候,其结构应该是,一个数组,而数组的每一个元素是一个单链表的头结点。大致如下图:
负载因子
负载因子值为0.75f,是HashMap的成员变量。由于数据越来越多,而表长不变的情况下,不同关键字通过相同的哈希函数计算出相同哈希地址的可能性将会越来越大,即哈希冲突越容易发生,为了避免发生冲突,负载因子的作用是当哈希表填入的元素个数达到哈希表长的3/4时,标志着要让哈希表扩容。
HashMap还有一个机制:当数组的长度大于64,或者链表的长度大于8的时候,HashMap会转换成TreeNode的方式,存储数据,以提高查找的效率。
HashSet
定义
HashSet是Set的一个实现类。
HashSet实现了Set接口,元素为<K>,并以哈希桶的结构进行存储数据。与Set接口一样,HashSet的底层就是HashMap。
原理
HashSet具有HashMap<E,Object>类型的成员变量map,它的成员方法,本质是都是对map进行操作。
同样的,它天然去重的特性,也是将Value值设为一个默认值。如下:
由于它底层由HashMap实现,那么它的许多增删特点也与HashMap相同。
注意点
二者自定义类作为K的时候,都必须重写equals与hashcode方法。
equals是为了在插入时,判断元素是否存在了,true替换。
hashcode是为了将类转换为int,以此确定hash函数后的值。