Java最全HashMap及HashTable源码解析,实践出真知

技术学习总结

学习技术一定要制定一个明确的学习路线,这样才能高效的学习,不必要做无效功,既浪费时间又得不到什么效率,大家不妨按照我这份路线来学习。

最后面试分享

大家不妨直接在牛客和力扣上多刷题,同时,我也拿了一些面试题跟大家分享,也是从一些大佬那里获得的,大家不妨多刷刷题,为金九银十冲一波!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

[java] view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public V put(K key, V value) {

  2. if (key == null)

  3. return putForNullKey(value); //处理null值

  4. int hash = hash(key.hashCode());//计算hash

  5. int i = indexFor(hash, table.length);//计算在数组中的存储位置

  6. //遍历table[i]位置的链表,查找相同的key,若找到则使用新的value替换掉原来的oldValue并返回oldValue

  7. for (Entry<K,V> e = table[i]; e != null; e = e.next) {

  8. Object k;

  9. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

  10. V oldValue = e.value;

  11. e.value = value;

  12. e.recordAccess(this);

  13. return oldValue;

  14. }

  15. }

  16. //若没有在table[i]位置找到相同的key,则添加key到table[i]位置,新的元素总是在table[i]位置的第一个元素,原来的元素后移

  17. modCount++;

  18. addEntry(hash, key, value, i);

  19. return null;

  20. }

a HashMap会对null值key进行特殊处理,总是放到table[0]位置

b计算has值

c 找到在table数组中的索引

1)遍历table[i]位置的链表,查找相同的key,若找到则使用新的value替换掉原来的oldValue并返回oldValue

2)若没有在table[i]位置找到相同的key,则添加key到table[i]位置,新的元素总是在table[i]位置的第一个元素,原来的元素后调用addEntry方法

[java] view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. void addEntry(int hash, K key, V value, int bucketIndex) {

  2. if ((size >= threshold) && (null != table[bucketIndex])) {

  3. resize(2 * table.length);

  4. hash = (null != key) ? hash(key) : 0;

  5. bucketIndex = indexFor(hash, table.length);

  6. }

  7. createEntry(hash, key, value, bucketIndex);

  8. }

[java] view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. void createEntry(int hash, K key, V value, int bucketIndex) {

  2. Entry<K,V> e = table[bucketIndex];

  3. table[bucketIndex] = new Entry<>(hash, key, value, e);

  4. size++;

  5. }

所做的工作就是:

1)判断当元素数量达到临界值(capactiy*factor)时,则进行扩容,是table数组长度变为table.length*2

2)当table[index]已存在其它元素时,会在table[index]位置形成一个链表,将新添加的元素放在table[index],原来的元素通过Entry的next进行链接,这样以链表形式解决hash冲突问题,

**2)get方法

**同样当key为null时会进行特殊处理,在table[0]的链表上查找key为null的元素

[java] view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public V get(Object key) {

  2. if (key == null)

  3. return getForNullKey();

  4. Entry<K,V> entry = getEntry(key);

  5. return null == entry ? null : entry.getValue();

  6. }

get的过程是先计算hash然后通过hash与table.length取摸计算index值,然后遍历table[index]上的链表,直到找到key,

然后返回,找不到返回null

[java] view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. final Entry<K,V> getEntry(Object key) {

  2. if (size == 0) {

  3. return null;

  4. }

  5. int hash = (key == null) ? 0 : hash(key);

  6. for (Entry<K,V> e = table[indexFor(hash, table.length)];

  7. e != null;

  8. e = e.next) {

  9. Object k;

  10. if (e.hash == hash &&

  11. ((k = e.key) == key || (key != null && key.equals(k))))

  12. return e;

  13. }

  14. return null;

  15. }

3)remove方法

remove方法和put get类似,计算hash,计算index,然后遍历查找,将找到的元素从table[index]链表移除

[java] view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public V remove(Object key) {

  2. Entry<K,V> e = removeEntryForKey(key);

  3. return (e == null ? null : e.value);

  4. }

[java] view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. final Entry<K,V> removeEntryForKey(Object key) {

  2. if (size == 0) {

  3. return null;

  4. }

  5. int hash = (key == null) ? 0 : hash(key);

  6. int i = indexFor(hash, table.length);

  7. Entry<K,V> prev = table[i];

  8. Entry<K,V> e = prev;

  9. while (e != null) {

  10. Entry<K,V> next = e.next;

  11. Object k;

  12. if (e.hash == hash &&

  13. ((k = e.key) == key || (key != null && key.equals(k)))) {

  14. modCount++;

  15. size–;

  16. if (prev == e)

  17. table[i] = next;

  18. else

  19. prev.next = next;

  20. e.recordRemoval(this);

  21. return e;

  22. }

  23. prev = e;

  24. e = next;

  25. }

  26. return e;

  27. }

4)clear()方法

clear方法非常简单,就是遍历table然后把每个位置置为null,同时修改元素个数为0

需要注意的是clear方法只会清楚里面的元素,并不会重置capactiy

[java] view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public void clear() {

  2. modCount++;

  3. Arrays.fill(table, null);

  4. size = 0;

  5. }

5)containsKey和containsValue

containsKey方法是先计算hash然后使用hash和table.length取摸得到index值,遍历table[index]元素查找是否包含key相同的值

[java] view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public boolean containsKey(Object key) {

  2. return getEntry(key) != null;

  3. }

[java] view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. final Entry<K,V> getEntry(Object key) {

  2. if (size == 0) {

  3. return null;

  4. }

  5. int hash = (key == null) ? 0 : hash(key);

  6. for (Entry<K,V> e = table[indexFor(hash, table.length)];

  7. e != null;

  8. e = e.next) {

  9. Object k;

  10. if (e.hash == hash &&

  11. ((k = e.key) == key || (key != null && key.equals(k))))

  12. return e;

  13. }

  14. return null;

  15. }

6)containsValue方法就比较粗暴了,就是直接遍历所有元素直到找到value,由此可见HashMap的containsValue方法本质上和普通数组和list的contains方法没什么区别,你别指望它会像containsKey那么高效

[java] view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public boolean containsValue(Object value) {

  2. // Same idea as size()

  3. if (value == null)

  4. throw new NullPointerException();

  5. final Segment<K,V>[] segments = this.segments;

  6. boolean found = false;

  7. long last = 0;

  8. int retries = -1;

  9. try {

  10. outer: for (;😉 {

  11. if (retries++ == RETRIES_BEFORE_LOCK) {

  12. for (int j = 0; j < segments.length; ++j)

  13. ensureSegment(j).lock(); // force creation

  14. }

  15. long hashSum = 0L;

  16. int sum = 0;

  17. for (int j = 0; j < segments.length; ++j) {

  18. HashEntry<K,V>[] tab;

  19. Segment<K,V> seg = segmentAt(segments, j);

  20. if (seg != null && (tab = seg.table) != null) {

  21. for (int i = 0 ; i < tab.length; i++) {

  22. HashEntry<K,V> e;

  23. for (e = entryAt(tab, i); e != null; e = e.next) {

  24. V v = e.value;

  25. if (v != null && value.equals(v)) {

  26. found = true;

  27. break outer;

  28. }

  29. }

  30. }

  31. sum += seg.modCount;

  32. }

  33. }

  34. if (retries > 0 && sum == last)

  35. break;

  36. last = sum;

  37. }

  38. finally {

  39. if (retries > RETRIES_BEFORE_LOCK) {

  40. for (int j = 0; j < segments.length; ++j)

  41. segmentAt(segments, j).unlock();

  42. }

  43. }

  44. return found;

  45. }

2 HashTable源码分析

1)构造方法有三个 ,带零个、一个、两个参数的,最终都会调用两个参数的构造方法

其思想也比较简单,检查参数的合法性

public Hashtable(int initialCapacity, float loadFactor) {

if (initialCapacity < 0)

throw new IllegalArgumentException("Illegal Capacity: "+

initialCapacity);

if (loadFactor <= 0 || Float.isNaN(loadFactor))

throw new IllegalArgumentException("Illegal Load: "+loadFactor);

if (initialCapacity==0)

initialCapacity = 1;

this.loadFactor = loadFactor;

table = new Entry[initialCapacity];

threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);

initHashSeedAsNeeded(initialCapacity);

}

2)put操作,直接在方法的前面加上synchronized 关键字,简单粗暴,在并发激烈的情况下,效率较低,所以才会有

ConcurrentHashMap的诞生。

public synchronized V put(K key, V value) {

// Make sure the value is not null

if (value == null) {

throw new NullPointerException();

}

// Makes sure the key is not already in the hashtable.

Entry tab[] = table;

int hash = hash(key);

int index = (hash & 0x7FFFFFFF) % tab.length;

for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {

if ((e.hash == hash) && e.key.equals(key)) {

V old = e.value;

e.value = value;

return old;

}

}

modCount++;

if (count >= threshold) {

// Rehash the table if the threshold is exceeded

rehash();

tab = table;

hash = hash(key);

index = (hash & 0x7FFFFFFF) % tab.length;

}

// Creates the new entry.

Entry<K,V> e = tab[index];

tab[index] = new Entry<>(hash, key, value, e);

count++;

return null;

}

3)get操作也一样,直接在访问该方法的时候上锁,确保同一个时刻只有一个线程能访问

public synchronized V get(Object key) {

Entry tab[] = table;

int hash = hash(key);

写在最后

很多人感叹“学习无用”,实际上之所以产生无用论,是因为自己想要的与自己所学的匹配不上,这也就意味着自己学得远远不够。无论是学习还是工作,都应该有主动性,所以如果拥有大厂梦,那么就要自己努力去实现它。

最后祝愿各位身体健康,顺利拿到心仪的offer!

由于文章的篇幅有限,所以这次的蚂蚁金服和京东面试题答案整理在了PDF文档里

蚂蚁、京东Java岗4面:原理+索引+底层+分布式+优化等,已拿offer

蚂蚁、京东Java岗4面:原理+索引+底层+分布式+优化等,已拿offer

蚂蚁、京东Java岗4面:原理+索引+底层+分布式+优化等,已拿offer

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

hold) {

// Rehash the table if the threshold is exceeded

rehash();

tab = table;

hash = hash(key);

index = (hash & 0x7FFFFFFF) % tab.length;

}

// Creates the new entry.

Entry<K,V> e = tab[index];

tab[index] = new Entry<>(hash, key, value, e);

count++;

return null;

}

3)get操作也一样,直接在访问该方法的时候上锁,确保同一个时刻只有一个线程能访问

public synchronized V get(Object key) {

Entry tab[] = table;

int hash = hash(key);

写在最后

很多人感叹“学习无用”,实际上之所以产生无用论,是因为自己想要的与自己所学的匹配不上,这也就意味着自己学得远远不够。无论是学习还是工作,都应该有主动性,所以如果拥有大厂梦,那么就要自己努力去实现它。

最后祝愿各位身体健康,顺利拿到心仪的offer!

由于文章的篇幅有限,所以这次的蚂蚁金服和京东面试题答案整理在了PDF文档里

[外链图片转存中…(img-rZPpVGKe-1715304213739)]

[外链图片转存中…(img-1szeFIJm-1715304213739)]

[外链图片转存中…(img-LKSxLZ1Q-1715304213740)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值