001/* 002 * Copyright (c) 2010-2024 Mark Allen, Norbert Bartels. 003 * 004 * Permission is hereby granted, free of charge, to any person obtaining a copy 005 * of this software and associated documentation files (the "Software"), to deal 006 * in the Software without restriction, including without limitation the rights 007 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 008 * copies of the Software, and to permit persons to whom the Software is 009 * furnished to do so, subject to the following conditions: 010 * 011 * The above copyright notice and this permission notice shall be included in 012 * all copies or substantial portions of the Software. 013 * 014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 019 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 020 * THE SOFTWARE. 021 */ 022/** 023 * This class is taken with friendly permission to use it 024 * from <a href="http://javaspecialists.co.za/archive/Issue098.html">javaspecialists.co.za/archive/Issue098.html</a> (section 'New SoftHashMap') 025 */ 026package com.restfb.util; 027 028import java.io.Serializable; 029import java.lang.ref.Reference; 030import java.lang.ref.ReferenceQueue; 031import java.lang.ref.SoftReference; 032import java.util.*; 033 034public class SoftHashMap<K, V> extends AbstractMap<K, V>implements Serializable { 035 036 private static final long serialVersionUID = 1L; 037 038 /** The internal HashMap that will hold the SoftReference. */ 039 private final Map<K, SoftReference<V>> hash = new HashMap<>(); 040 041 private final Map<SoftReference<V>, K> reverseLookup = new HashMap<>(); 042 043 /** Reference queue for cleared SoftReference objects. */ 044 private final ReferenceQueue<V> queue = new ReferenceQueue<>(); 045 046 @Override 047 public V get(Object key) { 048 expungeStaleEntries(); 049 V result = null; 050 // We get the SoftReference represented by that key 051 SoftReference<V> softRef = hash.get(key); 052 if (softRef != null) { 053 // From the SoftReference we get the value, which can be 054 // null if it has been garbage collected 055 result = softRef.get(); 056 if (result == null) { 057 // If the value has been garbage collected, remove the 058 // entry from the HashMap. 059 hash.remove(key); 060 reverseLookup.remove(softRef); 061 } 062 } 063 return result; 064 } 065 066 private void expungeStaleEntries() { 067 Reference<? extends V> sv; 068 while ((sv = queue.poll()) != null) { 069 hash.remove(reverseLookup.remove(sv)); 070 } 071 } 072 073 @Override 074 public V put(K key, V value) { 075 expungeStaleEntries(); 076 SoftReference<V> softRef = new SoftReference<>(value, queue); 077 reverseLookup.put(softRef, key); 078 SoftReference<V> result = hash.put(key, softRef); 079 if (result == null) { 080 return null; 081 } 082 reverseLookup.remove(result); 083 return result.get(); 084 } 085 086 @Override 087 public V remove(Object key) { 088 expungeStaleEntries(); 089 return Optional.ofNullable(hash.remove(key)).map(SoftReference::get).orElse(null); 090 } 091 092 @Override 093 public void clear() { 094 hash.clear(); 095 reverseLookup.clear(); 096 } 097 098 @Override 099 public int size() { 100 expungeStaleEntries(); 101 return hash.size(); 102 } 103 104 /** 105 * Returns a copy of the key/values in the map at the point of calling. However, setValue still sets the value in the 106 * actual SoftHashMap. 107 */ 108 @Override 109 public Set<Entry<K, V>> entrySet() { 110 expungeStaleEntries(); 111 Set<Entry<K, V>> result = new LinkedHashSet<>(); 112 for (final Entry<K, SoftReference<V>> entry : hash.entrySet()) { 113 final V value = entry.getValue().get(); 114 if (value != null) { 115 result.add(new Entry<K, V>() { 116 @Override 117 public K getKey() { 118 return entry.getKey(); 119 } 120 121 @Override 122 public V getValue() { 123 return value; 124 } 125 126 @Override 127 public V setValue(V v) { 128 entry.setValue(new SoftReference<>(v, queue)); 129 return value; 130 } 131 }); 132 } 133 } 134 return result; 135 } 136}