hashmap底层原理
hashmap在Java中被频繁使用到 ,了解hashmap底层原理有利于我们理解它的存储过程及快速使用。
1.哈希表
首先我们来认识一下哈希表,哈希表:是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
在哈希表中进行添加,删除,查找等操作,性能非常高,不考虑哈希冲突的情况下,一次定位即可完成,时间复杂度为O(1).
在两种物理存储结构中:顺序存储结构和链式存储结构,在数组中根据下标查找某个元素,一次就可以找到元素,哈希表的主干就是数组,这样提高了查找速度。
2.hashmap的数据结构
HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元,每一个Entry包含一个object类型的key-value键值对。对采用数组+链表的形式,充分运用了数组查询快,链表插入删除快的特性。
数组:查询速度快,可以根据索引查询;但插入和删除比较困难;
链表:查询速度慢,需要遍历整个链表,但插入和删除操作比较容易。
红黑树:一种二叉树,高效的检索效率,当链表达到一定长度后,链表就会变成红黑树,当链表长度大于8的时候,将后面的数据存在二叉树中
自己实现简易的hashmap
import java.util.HashMap;
public class WngMap {
HashMap<String, WngMap> ap=new HashMap<>();
SxtEntry[] arr=new SxtEntry[990];
int size;
public void put(Object key,Object value){
SxtEntry e=new SxtEntry(key, value);
for (int i = 0; i < size; i++) {
if(arr[i].key.equals(key)){
arr[i].value=value;
}
}
arr[size++]=e;
}
public Object get(Object key){
for (int i = 0; i < size; i++) {
if (arr[i].key.equals(key)) {
return arr[i].value;
}
}
return null;
}
public boolean contentKey(Object key){
for (int i = 0; i < size; i++) {
if(arr[i].key.equals(key)){
return true;
}
}
return false;
}
public boolean contentVlaue(Object value){
for (int i = 0; i < size; i++) {
if(arr[i].value.equals(value)){
return true;
}
}
return false;
}
public static void main(String[] args) {
WngMap w=new WngMap();
w.put("wng", "xq");
w.put("wngsy", "ali");
System.out.println(w.contentKey("wng"));
System.out.println(w.get("wngsy"));
}
}
class SxtEntry{
Object key;
Object value;
public SxtEntry(Object key, Object value) {
super();
this.key = key;
this.value = value;
}
}
通过例子来看一下它的存储过程,
如上图采用除留余数法来理解下hashmap的存储过程,对于散列表长为m的散列函数公式为:f( key ) = key mod p ( p ≤ m ) mod(取余)
因为有10条记录,我们就先让p=10,现在要把第一个元素51放入hashmap中,51%10=1 所以把它放入数组下标为1的链表中,链表中第一个位置已经存放1,把51接着在链表中的第二个位置中。
3.哈希冲突
如果两个不同的元素通过哈希函数得出的实际存储地址相同,要进行插入的时候,发现位置已经被其他元素占用了,这就是所谓的哈希冲突,也叫哈希碰撞。哈希函数的设计至关重要,哈希函数会尽可能地保证 计算简单和散列地址分布均匀,但是,数组是一块连续的固定长度的内存空间,再好的哈希函数也不能保证得到的存储地址绝对不发生冲突。采用链地址法可以解决哈希冲突,也就是数组+链表的方式,如上面所说的它的存储过程。
当不同对象计算出来的哈希值相同时,反生哈希冲突,链表增加一个节点,next指向下一个节点 。
4. 为什么重写equals方法需同时重写hashCode方法?
若我们把数组比作桶,桶中的list装有元素,equels直接比较的是元素。而我们在要比较桶中的元素是首先要用hashCode方法找到桶,才能用equals比较桶元素是否相等。否则可能出现取出的值为null的情况。这就是重写equals时也要同时覆盖hashcode”的原因。