Java之WeakHashMap原理及实际应用详解

2023-11-17 11:54:57 浏览数 (1)

哈喽,各位小伙伴们,你们好呀,我是喵手。

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

  如下是Java集合体系架构图,近期几期内容都是围绕该体系进行知识讲解,以便于同学们学习Java集合篇知识能够系统化而不零散。

在这里插入图片描述在这里插入图片描述

前言

  在Java开发中,我们经常需要使用Map来存储数据,而Java中提供的Map接口有多个实现类,如HashMapTreeMapLinkedHashMap等等。其中,WeakHashMap是一种特殊的实现类,它提供了一种可以自动回收key所占用的内存的机制。本文将介绍WeakHashMap的详细内容,并分析其应用场景、优缺点以及类代码方法介绍等。

摘要

  本文将介绍Java中的WeakHashMap,包括其定义、特点、源代码解析、应用场景案例、优缺点分析、类代码方法介绍、测试用例以及全文小结。

WeakHashMap

简介

  在Java中,WeakHashMap是一种继承自AbstractMap类,实现了Map接口的类。它与HashMap类似,也是一种散列表数据结构,但是它的key是弱引用类型,即如果一个key不再被其他对象所引用,那么这个key所对应的键值对就会被自动移除。因此WeakHashMap可以自动回收不再需要的key所占用的内存,从而避免内存泄漏的问题。

  WeakHashMap是线程不安全的,因此通常需要使用Collections.synchronizedMap方法将其封装成线程安全的Map。

源代码解析

  WeakHashMap的源代码非常复杂,这里只介绍其中的几个核心方法:

put方法

代码语言:java复制
public V put(K key, V value) {
    Objects.requireNonNull(key, "key == null");
    Objects.requireNonNull(value, "value == null");
    expungeStaleEntries();
    int hash = hash(key.hashCode());
    Entry<K,V>[] tab = table;
    int index = (tab.length - 1) & hash;
    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;
            e.recordAccess(this);
            return old;
        }
    }
    modCount  ;
    if (size >= threshold)
        resize(tab.length * 2);
    createEntry(hash, key, value, index);
    return null;
}

  put方法用于向WeakHashMap中添加一个键值对。首先,该方法会对key和value进行判空,然后调用expungeStaleEntries方法清除已经过时的键值对。接着,计算出key的哈希值,并根据该哈希值决定将该键值对存储到哪个桶中。如果该桶中已经存在一个与key相同的键值对,那么更新该键值对的value并返回旧的value;否则,将该键值对添加到桶中,并返回null。

在这里插入图片描述在这里插入图片描述

expungeStaleEntries方法

代码语言:java复制
private void expungeStaleEntries() {
    for (Object x; (x = queue.poll()) != null; ) {
        synchronized (queue) {
            @SuppressWarnings("unchecked")
            Entry<K,V> e = (Entry<K,V>) x;
            int i = indexFor(e.hash, table.length);
            Entry<K,V> prev = table[i];
            Entry<K,V> p = prev;
            while (p != null) {
                Entry<K,V> next = p.next;
                if (p == e) {
                    if (prev == e)
                        table[i] = next;
                    else
                        prev.next = next;
                    // Must null out stale entries to prevent
                    // interference with full GCs that occur during
                    // concurrent execution of the remapping methods.
                    e.value = null; // Help GC
                    size--;
                    break;
                }
                prev = p;
                p = next;
            }
        }
    }
}

  expungeStaleEntries方法用于清除已经过时的键值对。如果一个key不再被其他对象所引用,那么这个key所对应的键值对就会被添加到一个队列中。在每次添加新的键值对或者取出键值对时,该方法都会被调用一次,以清除已经过时的键值对。该方法会从队列中取出所有已经过时的键值对,并遍历哈希表中的所有桶,找到并清除所有的过时键值对。

在这里插入图片描述在这里插入图片描述

get方法

代码语言:java复制
public V get(Object key) {
    Object k = maskNull(key);
    int h = hash(k);
    Entry<K,V>[] tab = getTable();
    int index = indexFor(h, tab.length);
    Entry<K,V> e = tab[index];
    while (e != null) {
        if (e.hash == h && eq(k, e.get())) {
            V result = e.value;
            if (result != null)
                e.recordAccess(this);
            return result;
        }
        e = e.next;
    }
    return null;
}

  get方法用于根据key获取value。首先,该方法会将key进行判空,并计算出其哈希值。接着,根据哈希值找到该键值对所在的桶,并遍历该桶中的所有键值对,找到与key相同的键值对,返回其value;若没有找到,则返回null。

在这里插入图片描述在这里插入图片描述

应用场景案例

  WeakHashMap通常用于需要动态管理大量对象的应用中,可以避免因程序中某些对象不再需要而造成的内存泄漏问题。以下是一些使用WeakHashMap的应用场景:

  1. 缓存系统:可以使用WeakHashMap作为缓存的实现,这样在缓存中存储的键值对会在key不再被其他对象所引用时自动被移除,避免浪费内存;
  2. 生命周期管理:对于一些需要动态管理生命周期的对象,如数据库连接、线程池等,可以使用WeakHashMap来保存这些对象,避免因为忘记关闭连接或者资源而造成的内存泄漏问题;
  3. 监听器管理:在一些应用中,需要使用监听器来处理事件,这些监听器往往会注册到某些对象中去。如果不在需要时将这些监听器移除,就会造成内存泄漏。可以使用WeakHashMap来存储这些对象和其对应的监听器。当这些对象不再被其他对象所引用时,对应的监听器就会被自动移除。

优缺点分析

优点

  1. 可以避免内存泄漏问题:WeakHashMap使用弱引用保存key,当一个key不再被其他对象所引用时,对应的键值对会被自动移除,从而避免了内存泄漏问题;
  2. 在动态管理大量对象时具有很好的性能:由于WeakHashMap自动回收已经失效的键值对,因此可以避免内存占用过多的问题,从而提高应用的性能;
  3. 适用于一些需要动态管理对象的应用:如缓存系统、生命周期管理、监听器管理等。

缺点

  1. 线程不安全:WeakHashMap是线程不安全的,因此在多线程环境下需要使用Collections.synchronizedMap方法将其封装成线程安全的Map;
  2. 由于需要频繁回收已经失效的键值对,因此在性能上会比HashMap略差一些。

类代码方法介绍

以下是WeakHashMap类的部分方法介绍:

  1. void clear():用于清除WeakHashMap中所有的键值对;
  2. boolean containsKey(Object key):用于判断WeakHashMap是否包含指定的key;
  3. boolean containsValue(Object value):用于判断WeakHashMap是否包含指定的value;
  4. Set<Entry<K, V>> entrySet():返回WeakHashMap中所有键值对的Set视图;
  5. boolean equals(Object o):用于判断WeakHashMap是否与给定的对象相等;
  6. V get(Object key):根据key获取value;
  7. int hashCode():返回WeakHashMap的哈希码;
  8. boolean isEmpty():判断WeakHashMap是否为空;
  9. Set<K> keySet():返回WeakHashMap中所有key的Set视图;
  10. V put(K key, V value):将指定的key-value键值对放入WeakHashMap中;
  11. void putAll(Map<? extends K,? extends V> m):将m中所有的键值对放入WeakHashMap中;
  12. V remove(Object key):根据key移除WeakHashMap中对应的键值对;
  13. int size():返回WeakHashMap中键值对的数量;
  14. Collection<V> values():返回WeakHashMap中所有value的Collection视图;
  15. Object clone():返回WeakHashMap的副本;
  16. boolean remove(Object key, Object value):移除指定key-value键值对;
  17. boolean replace(K key, V oldValue, V newValue):替换指定key对应的value;
  18. V replace(K key, V value):替换指定key对应的value。

测试用例

以下是针对WeakHashMap的简单测试用例:

测试代码演示

代码语言:java复制
package com.example.javase.collection;

import java.util.Map;
import java.util.WeakHashMap;

/**
 * @author ms
 * @date 2023/10/25 16:26
 */
public class WeakHashMapTest {

    public static void main(String[] args) {
        Map<Number, String> map = new WeakHashMap<>();
        Integer key1 = new Integer(1);
        Float key2 = new Float(2.0f);
        Double key3 = new Double(3.0);
        map.put(key1, "value1");
        map.put(key2, "value2");
        map.put(key3, "value3");
        System.out.println("Before GC: "   map);
        key1 = null;
        key2 = null;
        key3 = null;
        System.gc();
        System.out.println("After GC: "   map);
    }
}

  在该测试用例中,我们创建了一个WeakHashMap,并向其中添加三个键值对。随后,将key1、key2、key3设置为null,并调用System.gc()方法进行垃圾回收。由于三个key均不再被其他对象所引用,因此这三个键值对会被自动移除。最后我们打印出WeakHashMap中剩余的键值对,可以看到只有一个键值对还存在。

测试结果

  根据如上测试用例,本地测试结果如下,仅供参考,你们也可以自行修改测试用例或者添加更多的测试数据或测试方法,进行熟练学习以此加深理解。

在这里插入图片描述在这里插入图片描述

测试代码分析

  根据如上测试用例,在此我给大家进行深入详细的解读一下测试代码,以便于更多的同学能够理解并加深印象。

  如上测试用例主要测试了Java中的WeakHashMap的特性。在代码中,首先创建了一个WeakHashMap实例,并在其中使用Integer、Float和Double类型的键值对进行了填充。然后输出了填充前的Map内容。接着将Integer、Float和Double类型的键设置为null,以便让它们成为垃圾对象,在执行完这一步操作后,调用Java的垃圾回收器进行垃圾回收,并输出了回收后的Map内容。

  WeakHashMap的一个重要特性是,当其键被设置为null或成为垃圾对象后,该键值对将自动从Map中删除。因此,当执行完key1 = null,key2 = null和key3 = null之后,这三个键值对将变成无法访问的垃圾对象,并在下一次垃圾回收器执行时被清除。

  因此,当进行垃圾回收后,输出的Map内容将不再包含这三个键值对,也就是只剩下一个空的Map。

全文小结

  本文介绍了Java中的WeakHashMap,包括其定义、特点、源代码解析、应用场景案例、优缺点分析、类代码方法介绍以及测试用例等。WeakHashMap是一种可以自动回收key所占用的内存的数据结构,在一些需要动态管理大量对象的应用中具有很好的性能表现。但是由于其线程不安全,需要进行额外的线程安全处理。

总结

  本文介绍了Java中的WeakHashMap,它是一种能够自动回收不再需要的key所占用的内存的数据结构。我们通过源代码解析、应用场景案例、优缺点分析、类代码方法介绍以及测试用例等方面详细介绍了WeakHashMap的定义、特点和使用方法。需要注意的是,WeakHashMap是线程不安全的,因此在多线程环境下需要进行额外的线程安全处理。强烈建议在合适的场景使用WeakHashMap来避免内存泄漏问题和提高应用的性能。

... ...

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

... ...

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。

⭐️若有疑问,就请评论留言告诉我叭。

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

0 人点赞