Java中内置有一个String.intern方法,对于重复使用的内容相同而内存地址不同的String对象,调用intern方法可以节省内存空间。如果将String作为HashMap的键来使用,intern可以提高性能。
那么今天我来向大家介绍一个针对Object的intern。Java没有提供这个方法,所以我实现了它。
package pers.laserpen.util.java;import java.util.Map.Entry;
import java.util.WeakHashMap;
import java.util.function.UnaryOperator;/*** 对象唯一引用,用于文件对比和文件操作的线程锁。如果同一进程有两个线程需要文件锁,那么需要先加线程锁。* * @author Laserpen*/
public class WeakIntern<TYPE> {private final WeakHashMap<TYPE, TYPE> m_normalObjectToInternObject = new WeakHashMap<>();private final WeakHashMap<TYPE, Object> m_internObjectStorage = new WeakHashMap<>();private final EquivalenceJudgment<TYPE> m_comparator;private final UnaryOperator<TYPE> m_internCreator;public WeakIntern(EquivalenceJudgment<TYPE> comparator, UnaryOperator<TYPE> internCreator) {m_comparator = comparator;m_internCreator = internCreator;}public synchronized final TYPE intern(final TYPE rawObject) {if (rawObject == null) {return null;}{TYPE internObject = m_normalObjectToInternObject.get(rawObject);// 已经有一个intern对象if (internObject != null) {return internObject;}}// 查找intern对象for (Entry<TYPE, Object> objectInInternSet : m_internObjectStorage.entrySet()) {if (rawObject == objectInInternSet) {// 不可以把已经在记录中的intern对象放入普通对象的映射表。否则gc不会回收这个intern对象。return rawObject;}boolean comp = compare(rawObject, objectInInternSet.getKey());// 相同文件共享intern对象if (comp) {m_normalObjectToInternObject.put(rawObject, objectInInternSet.getKey());return objectInInternSet.getKey();}}{ // 没有找到可用的intern对象,创建新intern对象TYPE internObject = m_internCreator.apply(rawObject);m_normalObjectToInternObject.put(rawObject, internObject);m_internObjectStorage.put(internObject, null);return internObject;}}private boolean compare(final TYPE a, final TYPE b) {if (a == b) {return true;}if (a == null) {return false;}if (b == null) {return false;}return m_comparator.areEquivalent(a, b);}@FunctionalInterfacepublic static interface EquivalenceJudgment<TYPE> {public boolean areEquivalent(TYPE o1, TYPE o2);}}
为什么我创建了一个接口EquivalenceJudgment,而不使用Object.equals方法?
因为有些对象的equals方法不完美。比如File.equals其实是对字符串的比较,指向同一文件的File对象如果一个是绝对路径,一个是相对路径,那么File.equals就会返回false。增加一个定制的EquivalenceJudgment接口是为了重新定义两个对象相等的条件。当然,对于大多数Java自带的类型,原本的equals还是可以使用的。
internCreator的存在则是为了提供创建intern对象的两种选择。你可以选择使用第一个对象当作intern对象,也可以复制一个。