`

读WeakHashMap源码

阅读更多
//一个基于弱引用的Map对象
//先看构造函数

public WeakHashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

public WeakHashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

public WeakHashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                DEFAULT_INITIAL_CAPACITY),
             DEFAULT_LOAD_FACTOR);
        putAll(m);
    }

public WeakHashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Initial Capacity: "+
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;

        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal Load factor: "+
                                               loadFactor);
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;
        table = newTable(capacity);
        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);
        useAltHashing = sun.misc.VM.isBooted() &&
                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
    }

//新增元素
 public V put(K key, V value) {
        //将key值为null的转变为对象
        Object k = maskNull(key);
	//计算hash值
        int h = hash(k);
	//获取整个entry在这里面会删掉过期的key
        Entry<K,V>[] tab = getTable();
        int i = indexFor(h, tab.length);

        for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
	   //如果key已经存在
            if (h == e.hash && eq(k, e.get())) {
                V oldValue = e.value;
                if (value != oldValue)
                    e.value = value;
                return oldValue;
            }
        }

        modCount++;
        Entry<K,V> e = tab[i];
	//插入到第一个位置
        tab[i] = new Entry<>(k, value, queue, h, e);
	//扩容
        if (++size >= threshold)
            resize(tab.length * 2);
        return null;
    }

 private static Object maskNull(Object key) {
        return (key == null) ? NULL_KEY : key;
    }
 private static final Object NULL_KEY = new Object();

private Entry<K,V>[] getTable() {
        expungeStaleEntries();
        return table;
    }

//删掉过期的key
 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 not null out e.next;
                        // stale entries may be in use by a HashIterator
			//防止内存泄漏
                        e.value = null; // Help GC
                        size--;
                        break;
                    }
                    prev = p;
                    p = next;
                }
            }
        }
    }

 void resize(int newCapacity) {
        Entry<K,V>[] oldTable = getTable();
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry<K,V>[] newTable = newTable(newCapacity);
        boolean oldAltHashing = useAltHashing;
        useAltHashing |= sun.misc.VM.isBooted() &&
                (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        boolean rehash = oldAltHashing ^ useAltHashing;
        transfer(oldTable, newTable, rehash);
        table = newTable;

       
        if (size >= threshold / 2) {
            threshold = (int)(newCapacity * loadFactor);
        } else {
            expungeStaleEntries();
            transfer(newTable, oldTable, false);
            table = oldTable;
        }
    }

//构造新的entry
  private void transfer(Entry<K,V>[] src, Entry<K,V>[] dest, boolean rehash) {
        for (int j = 0; j < src.length; ++j) {
            Entry<K,V> e = src[j];
            src[j] = null;
            while (e != null) {
                Entry<K,V> next = e.next;
                Object key = e.get();
                if (key == null) {
                    e.next = null;  // Help GC
                    e.value = null; //  "   "
                    size--;
                } else {
                    if (rehash) {
                        e.hash = hash(key);
                    }
                    int i = indexFor(e.hash, dest.length);
                    e.next = dest[i];
                    dest[i] = e;
                }
                e = next;
            }
        }
    }

/** 插入元素的过程就是上面这样,现在的问题是自动回收怎么回收?*/
//看插入时候的这句代码
tab[i] = new Entry<>(k, value, queue, h, e);
//然后就看到这个entry
 private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
        V value;
        int hash;
        Entry<K,V> next;

        /**
         * Creates new entry.
         */
        Entry(Object key, V value,
              ReferenceQueue<Object> queue,
              int hash, Entry<K,V> next) {
            super(key, queue);
            this.value = value;
            this.hash  = hash;
            this.next  = next;
        }

//然后就是
public abstract class Reference<T> 
//里面有一个线程
private static class ReferenceHandler extends Thread {

        ReferenceHandler(ThreadGroup g, String name) {
            super(g, name);
        }

        public void run() {
            for (;;) {

                Reference r;
                synchronized (lock) {
                    if (pending != null) {
		       //由虚拟机赋值
                        r = pending;
                        Reference rn = r.next;
                        pending = (rn == r) ? null : rn;
                        r.next = r;
                    } else {
                        try {
                            lock.wait();
                        } catch (InterruptedException x) { }
                        continue;
                    }
                }

                // Fast path for cleaners
                if (r instanceof Cleaner) {
                    ((Cleaner)r).clean();
                    continue;
                }

                ReferenceQueue q = r.queue;
		//入队
                if (q != ReferenceQueue.NULL) q.enqueue(r);
            }
        }
    }



boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
        synchronized (r) {
            if (r.queue == ENQUEUED) return false;
            synchronized (lock) {
                r.queue = ENQUEUED;
                r.next = (head == null) ? r : head;
                head = r;
                queueLength++;
                if (r instanceof FinalReference) {
                    sun.misc.VM.addFinalRefCount(1);
                }
                lock.notifyAll();
                return true;
            }
        }
    }

/**
总结:每次在put一个元素的时候,首先会从ReferenceQueue中找出已经过期的键然后删除掉。
然后每次在新建的时候entry的时候后台会通过ThreadGroup来管理一组线程。这组线程主要是
jvm给Reference赋值已经过期的key。然后加入到ReferenceQueue的队列中。
*/


public void putAll(Map<? extends K, ? extends V> m) {
        int numKeysToBeAdded = m.size();
        if (numKeysToBeAdded == 0)
            return;

        //先计算是否需要扩容 提升效率
        if (numKeysToBeAdded > threshold) {
            int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
            if (targetCapacity > MAXIMUM_CAPACITY)
                targetCapacity = MAXIMUM_CAPACITY;
            int newCapacity = table.length;
            while (newCapacity < targetCapacity)
                newCapacity <<= 1;
            if (newCapacity > table.length)
                resize(newCapacity);
        }

        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

//根据键获取值
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()))
                return e.value;
            e = e.next;
        }
        return null;
    }

//返回长度
public int size() {
        if (size == 0)
            return 0;
        expungeStaleEntries();
        return size;
    }

//整个map是否为空
public boolean isEmpty() {
        return size() == 0;
    }

//是否包含某个键
 public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }

  Entry<K,V> getEntry(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 && !(e.hash == h && eq(k, e.get())))
            e = e.next;
        return e;
    }


//根据key删除
public V remove(Object key) {
        Object k = maskNull(key);
        int h = hash(k);
        Entry<K,V>[] tab = getTable();
        int i = indexFor(h, tab.length);
        Entry<K,V> prev = tab[i];
        Entry<K,V> e = prev;

        while (e != null) {
            Entry<K,V> next = e.next;
            if (h == e.hash && eq(k, e.get())) {
                modCount++;
                size--;
                if (prev == e)
                    tab[i] = next;
                else
                    prev.next = next;
                return e.value;
            }
            prev = e;
            e = next;
        }

        return null;
    }


//删除entry
boolean removeMapping(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Entry<K,V>[] tab = getTable();
        Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
        Object k = maskNull(entry.getKey());
        int h = hash(k);
        int i = indexFor(h, tab.length);
        Entry<K,V> prev = tab[i];
        Entry<K,V> e = prev;

        while (e != null) {
            Entry<K,V> next = e.next;
            if (h == e.hash && e.equals(entry)) {
                modCount++;
                size--;
                if (prev == e)
                    tab[i] = next;
                else
                    prev.next = next;
                return true;
            }
            prev = e;
            e = next;
        }

        return false;
    }

//清空
public void clear() {
        
        while (queue.poll() != null)
            ;

        modCount++;
        Arrays.fill(table, null);
        size = 0;

        
        while (queue.poll() != null)
            ;
    }

//是否包含某个值
public boolean containsValue(Object value) {
        if (value==null)
            return containsNullValue();

        Entry<K,V>[] tab = getTable();
        for (int i = tab.length; i-- > 0;)
            for (Entry<K,V> e = tab[i]; e != null; e = e.next)
                if (value.equals(e.value))
                    return true;
        return false;
    }

分享到:
评论

相关推荐

    WeakHashMap的使用方法详解

    WeakHashMap的使用方法详解 WeakHashMap是Java中的一种哈希映射表,它的键是弱引用的,意味着当 WeakHashMap 的键对象只有 WeakHashMap 自己持有时,垃圾回收器可以将其回收。WeakHashMap 的使用方法主要体现在...

    java集合-WeakHashMap的使用

    WeakHashMap是Java中的一种特殊的哈希表实现,它使用弱引用(Weak Reference)来保存键对象。当键对象没有被其他强引用引用时,在垃圾回收时会自动从WeakHashMap中移除对应的键值对。

    Java编程WeakHashMap实例解析

    Java编程WeakHashMap实例解析 WeakHashMap是Java编程中的一种特殊的HashMap实现,它使用弱引用来保存键和值,这样可以使得垃圾回收器自动清理键和值。在WeakHashMap中,键和值都是弱引用的,这样可以避免内存泄露...

    清华妹子的Java仓库(进阶学习路线)

    本仓库记录了我的Java学习进阶之路,涵盖了Java基础、JDK源码、JVM中的重要知识,附有代码和博客讲解,旨在提供一个Java在线共享学习平台,帮助更多的...Java集合框架源码解读(4)——WeakHashMap Java集合框架源码解读

    应用源码之douBanList(滚动到底部加载新的,软缓存,懒加载).zip

    在Android中,这通常通过WeakHashMap实现,它可以存储对象引用,当对象不再被其他地方引用时,会被垃圾回收器自动清理,避免内存泄漏。douBanList应用可能将网络请求的数据保存在内存中,当用户再次访问时,优先从...

    Map-main-源码.rar

    HashMap是非线程安全的,适合于高并发环境下的读操作。 二、TreeMap TreeMap使用红黑树作为底层数据结构,提供了有序的键值对存储。红黑树是一种自平衡的二叉查找树,它保证了插入、删除和查找的时间复杂度都是O...

    javabitset源码-JerrySoundCode:杰瑞声码

    bitset源码Java源码分析 基础集合列表 ArrayList (done) Vector (done) LinkedList (done) Stack (done) ReferenceQueue (done) ArrayDeque (done) Set HashSet (done) TreeSet (done) LinkedHashSet (done) BitSet ...

    Android高级应用源码-下载网络图片 (整合多线程、内存缓存、本地文件缓存~).rar

    源码中可能实现了类似的功能,使用`WeakHashMap`或者`LRUCache`作为内存缓存策略。 4. **本地文件缓存**: - 当内存不足以存储所有图片时,本地文件缓存是必要的。Android提供了`SQLite`数据库、`内部存储`和`外部...

    Android ListView分页功能源码.rar

    内存缓存(如WeakHashMap)和数据库缓存(如SQLite)是两种常见的缓存方式。 7. **空页面处理**:在没有更多数据可加载时,应当给出提示,避免用户无休止地等待加载。这可以通过设置一个空视图(empty view)来实现...

    DiskLrucache 源码 及jar包.rar

    在Android应用中,`DiskLruCache`常被用来作为内存缓存(如`Android`的`WeakHashMap`或`LruCache`)的补充,以提高数据读取性能和减少网络请求。 源码分析: 1. **LRU策略**:LRU策略是`DiskLruCache`的核心,它的...

    Android应用源码之android端用于异步加载图片

    在Android中,可以使用WeakHashMap或LruCache(自Android 3.0引入)作为内存缓存的实现。LruCache通过LRU(Least Recently Used)策略管理缓存,当缓存满时,会删除最近最少使用的图片。 3. **文件缓存**: - 文件...

    Java Reference源码解析

    实际的应用见WeakHashMap等。 4. 虚引用(PhantomReference):虚引用,该引用必须和引用队列(ReferenceQueue)一起使用,一般用于实现追踪垃圾收集器的回收动作,比如在对象被回收的时候,会调用该对象的finalize...

    Java集合源码全面分析

    理解Java集合框架的源码对于提升Java编程能力至关重要,这有助于优化代码性能,减少潜在问题,并更好地理解和利用框架提供的功能。深入研究这些接口和类的实现细节,能够帮助开发者在实际项目中做出更明智的选择。...

    Java弱引用与WeakHashMap

    书中还提到可以用WeakHashMap来作为缓存的容器可以有效解决这一问题。之前也确实遇到过类似问题,但是没有接触过“弱引用”相关的问题,于是查阅了一些资料。  《Java 理论与实践: 用弱引用堵住内存泄漏》一文也...

    Android高级应用源码-Android 图片缓存、加载器.rar

    本资源“Android高级应用源码-Android 图片缓存、加载器”提供了关于这个主题的源代码示例,旨在帮助开发者深入理解并优化图片处理流程。以下是基于这个主题的详细知识点讲解: 1. **内存缓存**: - 内存缓存是一...

    Android应用源码之图片异步缓存两层缓存.zip

    本资料"Android应用源码之图片异步缓存两层缓存.zip"提供了一个实现这一功能的示例,其中包含一个名为"ListViewPerformance0.2"的项目,很可能是针对ListView性能优化的一个版本。下面将详细介绍这个项目可能涉及的...

    安卓Android源码——从网络上获取图片.zip

    这份“安卓Android源码——从网络上获取图片.zip”压缩包很可能是提供了一个示例项目或代码片段,用于演示如何在Android应用中实现这一功能。现在我们将详细讨论这个过程中的关键知识点。 1. **网络请求**: - **...

    Android应用源码之douBanList(滚动到底部加载新的,软缓存,懒加载).zip

    其次,软缓存(Soft Cache)是一种内存级别的缓存策略,它利用Java的WeakHashMap存储数据,以避免内存泄漏。在这种缓存中,当系统内存紧张时,这些缓存对象会被自动回收,从而保证应用的内存管理效率。在Android的...

    Android源码——douBanList(滚动到底部加载新的,软缓存,懒加载).zip

    在Android中,可以使用WeakHashMap或者LRUCache(Least Recently Used Cache)来实现软缓存。当新的数据请求到来时,系统首先检查缓存中是否存在所需的数据,如果存在则直接返回,避免了不必要的网络请求和数据处理...

Global site tag (gtag.js) - Google Analytics