Java hashmap

xiaoxiao2021-02-28  81

Java hashmap

本文基于Java 1.7 进行分析,这次分析主要是作为自己的笔记留存。

hashmap的基本原理

HashMap基于哈希算法,使用put存放对象,get获取对象,在初始设置hashmap容量时,尽量设置为2的幂次方

get,用户传入K,然后根据hash值,计算位置,确定在桶中的位置,然后获取value,中间需要通过K的equals()方法,hash值,key是否相等,来确定是否是取到的值。put,用户将K-V传递给put后,通过K对象的hashcode()方法来计算本次存储的hash值,然后通过indexFor()方法,获取需要存入hash桶的位置,hash桶是一个数组,默认初始大小是16,里面存放的是Entry

put

public V put(K key, V value) { //如果table为空,使用默认值设置talbe数组和门限threshold if (table == EMPTY_TABLE) { inflateTable(threshold); } //key可以为空,处理key为空的情况,从这里可以看出为什么HashMap允许key为空 if (key == null) return putForNullKey(value); //通过key获取hash值,hash方法里面用到了key的hashcode()方法 int hash = hash(key); //获取在桶中的位置 int i = indexFor(hash, table.length); //遍历hash桶,判断是否已经有值,本次是重新写入,还是已经hash冲突了 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; //hash值相同且对象相同时,认为是重新写入值,覆盖旧值,hash冲突时判断对象是否相等,来区分是否覆盖写入 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; //在hash桶中添加对象以及解决hash冲突 addEntry(hash, key, value, i); return null; }

通过上文源码以及注释可以看到,在使用hashmap时,需要作为key的对象,必须实现了hashcode和equals两个方法,保证key的唯一性,因为在hashmap中,使用hashcode来确定位置,使用equals来确定是否重复,以及区分hash冲突的情况,因为hash冲突时,一般是hashcode值一样导致,但是两个对象通过equals方法,是可以确实是不等的,这也是为什么String为什么适合作为key的原因。

在put中有一个addEntry方法,这个方法用来将k-v存入hash桶,包括了hash冲突时的处理。

void addEntry(int hash, K key, V value, int bucketIndex) { //如果桶超过限制,一般为容量*0.75,就resize桶大小,扩展为2 * 当前大小 if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length); hash = (null != key) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length); } //创建Entry对象,并写入桶中 createEntry(hash, key, value, bucketIndex); } void createEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<>(hash, key, value, e); size++; } Entry(int h, K k, V v, Entry<K,V> n) { value = v; next = n; key = k; hash = h; }

通过上文代码可以看到,Entry是实际存储K-V的对象,同时也是用来解决hash冲突的单链表,当我们遇到hash冲突时,例如A,B,A已经存在桶中13这个位置,A,B hash冲突,这是,就是将B写入到A目前的位置,同时将A放入Entry的next的位置,链接起来。

get

public V get(Object key) { if (key == null) return getForNullKey(); //获取Entry<K,V>对象 Entry<K,V> entry = getEntry(key); return null == entry ? null : entry.getValue(); } final Entry<K,V> getEntry(Object key) { //如果size为0,返回null if (size == 0) { return null; } //计算hash值 int hash = (key == null) ? 0 : hash(key); //在桶中指定位置,遍历Entry链表,获取需要的值 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; //根据条件判断是否是需要的值 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) //返回需要的对象 return e; } //未找到 返回null return null; }
转载请注明原文地址: https://www.6miu.com/read-59937.html

最新回复(0)