add ReferenceConcurrentMap

This commit is contained in:
Looly
2024-03-23 12:07:28 +08:00
parent af85d8beaf
commit 082a8f67ac
6 changed files with 528 additions and 97 deletions

View File

@@ -1056,6 +1056,21 @@ public class MapUtil extends MapGetUtil {
return removeIf(map, entry -> null == entry.getValue());
}
/**
* 去除Map中值为指定值的键值对<br>
* 注意此方法在传入的Map上直接修改。
*
* @param <K> key的类型
* @param <V> value的类型
* @param map Map
* @param value 给定值
* @return map
* @since 6.0.0
*/
public static <K, V> Map<K, V> removeByValue(final Map<K, V> map, final V value) {
return removeIf(map, entry -> ObjUtil.equals(value, entry.getValue()));
}
/**
* 去除Map中值为{@code null}的键值对<br>
* 注意此方法在传入的Map上直接修改。

View File

@@ -0,0 +1,373 @@
/*
* Copyright (c) 2023-2024. looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* https://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.map.reference;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.util.ReferenceUtil;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* 线程安全的ReferenceMap实现
*
* @param <K> 键类型
* @param <V> 值类型
* @author looly
*/
public abstract class ReferenceConcurrentMap<K, V> implements ConcurrentMap<K, V>, Iterable<Map.Entry<K, V>>, Serializable {
private static final long serialVersionUID = 1L;
final ConcurrentMap<Reference<K>, Reference<V>> raw;
private final ReferenceQueue<K> lastKeyQueue;
private final ReferenceQueue<V> lastValueQueue;
/**
* 回收监听
*/
private BiConsumer<Reference<? extends K>, Reference<? extends V>> purgeListener;
// region 构造
/**
* 构造
*
* @param raw {@link ConcurrentMap}实现
*/
public ReferenceConcurrentMap(final ConcurrentMap<Reference<K>, Reference<V>> raw) {
this.raw = raw;
lastKeyQueue = new ReferenceQueue<>();
lastValueQueue = new ReferenceQueue<>();
}
// endregion
/**
* 设置对象回收清除监听
*
* @param purgeListener 监听函数
*/
public void setPurgeListener(final BiConsumer<Reference<? extends K>, Reference<? extends V>> purgeListener) {
this.purgeListener = purgeListener;
}
@Override
public int size() {
this.purgeStale();
return this.raw.size();
}
@Override
public boolean isEmpty() {
this.purgeStale();
return this.raw.isEmpty();
}
@Override
public V get(final Object key) {
this.purgeStale();
return unwrap(this.raw.get(wrapKey(key)));
}
@Override
public boolean containsKey(final Object key) {
this.purgeStale();
return this.raw.containsKey(wrapKey(key));
}
@Override
public boolean containsValue(final Object value) {
this.purgeStale();
return this.raw.containsValue(wrapValue(value));
}
@Override
public V put(final K key, final V value) {
this.purgeStale();
final Reference<V> vReference = this.raw.put(wrapKey(key), wrapValue(value));
return unwrap(vReference);
}
@Override
public V putIfAbsent(final K key, final V value) {
this.purgeStale();
final Reference<V> vReference = this.raw.putIfAbsent(wrapKey(key), wrapValue(value));
return unwrap(vReference);
}
@Override
public void putAll(final Map<? extends K, ? extends V> m) {
m.forEach(this::put);
}
@Override
public V replace(final K key, final V value) {
this.purgeStale();
final Reference<V> vReference = this.raw.replace(wrapKey(key), wrapValue(value));
return unwrap(vReference);
}
@Override
public boolean replace(final K key, final V oldValue, final V newValue) {
this.purgeStale();
return this.raw.replace(wrapKey(key), wrapValue(oldValue), wrapValue(newValue));
}
@Override
public void replaceAll(final BiFunction<? super K, ? super V, ? extends V> function) {
this.purgeStale();
this.raw.replaceAll((rKey, rValue) -> wrapValue(function.apply(unwrap(rKey), unwrap(rValue))));
}
@Override
public V computeIfAbsent(final K key, final Function<? super K, ? extends V> mappingFunction) {
this.purgeStale();
final Reference<V> vReference = this.raw.computeIfAbsent(wrapKey(key),
kReference -> wrapValue(mappingFunction.apply(unwrap(kReference))));
return unwrap(vReference);
}
@Override
public V computeIfPresent(final K key, final BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
this.purgeStale();
final Reference<V> vReference = this.raw.computeIfPresent(wrapKey(key),
(kReference, vReference1) -> wrapValue(remappingFunction.apply(unwrap(kReference), unwrap(vReference1))));
return unwrap(vReference);
}
@Override
public V remove(final Object key) {
this.purgeStale();
return unwrap(this.raw.remove(wrapKey(key)));
}
@SuppressWarnings("unchecked")
@Override
public boolean remove(final Object key, final Object value) {
this.purgeStale();
return this.raw.remove(wrapKey((K) key, null), value);
}
@SuppressWarnings("StatementWithEmptyBody")
@Override
public void clear() {
this.raw.clear();
while (lastKeyQueue.poll() != null) ;
while (lastValueQueue.poll() != null) ;
}
@Override
public Set<K> keySet() {
this.purgeStale();
final Set<Reference<K>> referenceSet = this.raw.keySet();
return new AbstractSet<K>() {
@Override
public Iterator<K> iterator() {
final Iterator<Reference<K>> referenceIter = referenceSet.iterator();
return new Iterator<K>() {
@Override
public boolean hasNext() {
return referenceIter.hasNext();
}
@Override
public K next() {
return unwrap(referenceIter.next());
}
};
}
@Override
public int size() {
return referenceSet.size();
}
};
}
@Override
public Collection<V> values() {
this.purgeStale();
final Collection<Reference<V>> referenceValues = this.raw.values();
return new AbstractCollection<V>() {
@Override
public Iterator<V> iterator() {
final Iterator<Reference<V>> referenceIter = referenceValues.iterator();
return new Iterator<V>() {
@Override
public boolean hasNext() {
return referenceIter.hasNext();
}
@Override
public V next() {
return unwrap(referenceIter.next());
}
};
}
@Override
public int size() {
return referenceValues.size();
}
};
}
@Override
public Set<Entry<K, V>> entrySet() {
this.purgeStale();
final Set<Entry<Reference<K>, Reference<V>>> referenceEntrySet = this.raw.entrySet();
return new AbstractSet<Entry<K, V>>() {
@Override
public Iterator<Entry<K, V>> iterator() {
final Iterator<Entry<Reference<K>, Reference<V>>> referenceIter = referenceEntrySet.iterator();
return new Iterator<Entry<K, V>>() {
@Override
public boolean hasNext() {
return referenceIter.hasNext();
}
@Override
public Entry<K, V> next() {
final Entry<Reference<K>, Reference<V>> next = referenceIter.next();
return new Entry<K, V>() {
@Override
public K getKey() {
return unwrap(next.getKey());
}
@Override
public V getValue() {
return unwrap(next.getValue());
}
@Override
public V setValue(final V value) {
return unwrap(next.setValue(wrapValue(value)));
}
};
}
};
}
@Override
public int size() {
return referenceEntrySet.size();
}
};
}
@Override
public void forEach(final BiConsumer<? super K, ? super V> action) {
this.purgeStale();
this.raw.forEach((key, rValue) -> action.accept(key.get(), unwrap(rValue)));
}
@Override
public Iterator<Entry<K, V>> iterator() {
return entrySet().iterator();
}
@Override
public V compute(final K key, final BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
this.purgeStale();
return unwrap(this.raw.compute(wrapKey(key),
(kReference, vReference) -> wrapValue(remappingFunction.apply(unwrap(kReference), unwrap(vReference)))));
}
@Override
public V merge(final K key, final V value, final BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
this.purgeStale();
return unwrap(this.raw.merge(wrapKey(key), wrapValue(value),
(vReference, vReference2) -> wrapValue(remappingFunction.apply(unwrap(vReference), unwrap(vReference2)))));
}
/**
* 清除被回收的键和值
*/
@SuppressWarnings("unchecked")
private void purgeStale() {
Reference<? extends K> key;
Reference<? extends V> value;
// 清除无效key对应键值对
while ((key = this.lastKeyQueue.poll()) != null) {
value = this.raw.remove(key);
if (null != purgeListener) {
purgeListener.accept(key, value);
}
}
// 清除无效value对应的键值对
while ((value = this.lastValueQueue.poll()) != null) {
MapUtil.removeByValue(this.raw, (Reference<V>) value);
if (null != purgeListener) {
purgeListener.accept(null, value);
}
}
}
/**
* 根据Reference类型构建key对应的{@link Reference}
*
* @param key 键
* @param queue {@link ReferenceQueue}
* @return {@link Reference}
*/
abstract Reference<K> wrapKey(final K key, final ReferenceQueue<? super K> queue);
/**
* 根据Reference类型构建value对应的{@link Reference}
*
* @param value 值
* @param queue {@link ReferenceQueue}
* @return {@link Reference}
*/
abstract Reference<V> wrapValue(final V value, final ReferenceQueue<? super V> queue);
/**
* 根据Reference类型构建key对应的{@link Reference}
*
* @param key 键
* @return {@link Reference}
*/
@SuppressWarnings("unchecked")
private Reference<K> wrapKey(final Object key) {
return wrapKey((K) key, this.lastKeyQueue);
}
/**
* 根据Reference类型构建value对应的{@link Reference}
*
* @param value 键
* @return {@link Reference}
*/
@SuppressWarnings("unchecked")
private Reference<V> wrapValue(final Object value) {
return wrapValue((V) value, this.lastValueQueue);
}
/**
* 去包装对象
*
* @param <T> 对象类型
* @param obj 对象
* @return 值
*/
private static <T> T unwrap(final Reference<T> obj) {
return ReferenceUtil.get(obj);
}
}

View File

@@ -13,20 +13,12 @@
package org.dromara.hutool.core.map.reference;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.core.util.ReferenceUtil;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@@ -244,80 +236,10 @@ public class ReferenceKeyConcurrentMap<K, V> implements ConcurrentMap<K, V>, Ite
private Reference<K> ofKey(final K key, final ReferenceQueue<? super K> queue) {
switch (keyType) {
case WEAK:
return new WeakKey<>(key, queue);
return new WeakObj<>(key, queue);
case SOFT:
return new SoftKey<>(key, queue);
return new SoftObj<>(key, queue);
}
throw new IllegalArgumentException("Unsupported key type: " + keyType);
}
/**
* 弱键
*
* @param <K> 键类型
*/
private static class WeakKey<K> extends WeakReference<K> {
private final int hashCode;
/**
* 构造
*
* @param key 原始Key不能为{@code null}
* @param queue {@link ReferenceQueue}
*/
WeakKey(final K key, final ReferenceQueue<? super K> queue) {
super(key, queue);
hashCode = key.hashCode();
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(final Object other) {
if (other == this) {
return true;
} else if (other instanceof WeakKey) {
return ObjUtil.equals(((WeakKey<?>) other).get(), get());
}
return false;
}
}
/**
* 弱键
*
* @param <K> 键类型
*/
private static class SoftKey<K> extends SoftReference<K> {
private final int hashCode;
/**
* 构造
*
* @param key 原始Key不能为{@code null}
* @param queue {@link ReferenceQueue}
*/
SoftKey(final K key, final ReferenceQueue<? super K> queue) {
super(key, queue);
hashCode = key.hashCode();
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(final Object other) {
if (other == this) {
return true;
} else if (other instanceof SoftKey) {
return ObjUtil.equals(((SoftKey<?>) other).get(), get());
}
return false;
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2024. looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* https://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.map.reference;
import org.dromara.hutool.core.util.ObjUtil;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
/**
* 弱键
*
* @param <T> 键类型
*/
public class SoftObj<T> extends SoftReference<T> {
private final int hashCode;
/**
* 构造
*
* @param key 原始Key不能为{@code null}
* @param queue {@link ReferenceQueue}
*/
public SoftObj(final T key, final ReferenceQueue<? super T> queue) {
super(key, queue);
hashCode = key.hashCode();
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(final Object other) {
if (other == this) {
return true;
} else if (other instanceof SoftObj) {
return ObjUtil.equals(((SoftObj<?>) other).get(), get());
}
return false;
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2024. looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* https://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.map.reference;
import org.dromara.hutool.core.util.ObjUtil;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
/**
* 弱键
*
* @param <T> 键类型
*/
public class WeakObj<T> extends WeakReference<T> {
private final int hashCode;
/**
* 构造
*
* @param key 原始Key不能为{@code null}
* @param queue {@link ReferenceQueue}
*/
public WeakObj(final T key, final ReferenceQueue<? super T> queue) {
super(key, queue);
hashCode = key.hashCode();
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(final Object other) {
if (other == this) {
return true;
} else if (other instanceof WeakObj) {
return ObjUtil.equals(((WeakObj<?>) other).get(), get());
}
return false;
}
}

View File

@@ -35,8 +35,8 @@ public class ReferenceUtil {
/**
* 获得引用
*
* @param <T> 被引用对象类型
* @param type 引用类型枚举
* @param <T> 被引用对象类型
* @param type 引用类型枚举
* @param referent 被引用对象
* @return {@link Reference}
*/
@@ -47,35 +47,50 @@ public class ReferenceUtil {
/**
* 获得引用
*
* @param <T> 被引用对象类型
* @param type 引用类型枚举
* @param <T> 被引用对象类型
* @param type 引用类型枚举
* @param referent 被引用对象
* @param queue 引用队列
* @param queue 引用队列
* @return {@link Reference}
*/
public static <T> Reference<T> of(final ReferenceType type, final T referent, final ReferenceQueue<T> queue) {
switch (type) {
case SOFT:
return new SoftReference<>(referent, queue);
case WEAK:
return new WeakReference<>(referent, queue);
case PHANTOM:
return new PhantomReference<>(referent, queue);
default:
return null;
case SOFT:
return new SoftReference<>(referent, queue);
case WEAK:
return new WeakReference<>(referent, queue);
case PHANTOM:
return new PhantomReference<>(referent, queue);
default:
return null;
}
}
/**
* {@code null}全的解包获取原始对象
*
* @param <T> 对象类型
* @param obj Reference对象
* @return 原始对象 or {@code null}
* @since 6.0.0
*/
public static <T> T get(final Reference<T> obj) {
return ObjUtil.apply(obj, Reference::get);
}
/**
* 引用类型
*
* @author looly
*
*/
public enum ReferenceType {
/** 软引用在GC报告内存不足时会被GC回收 */
/**
* 软引用在GC报告内存不足时会被GC回收
*/
SOFT,
/** 弱引用在GC时发现弱引用会回收其对象 */
/**
* 弱引用在GC时发现弱引用会回收其对象
*/
WEAK,
/**
* 虚引用在GC时发现虚引用对象会将{@link PhantomReference}插入{@link ReferenceQueue}。 <br>