Java Object intern算法

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;

/**
 * 对象唯一引用。<br/>
 * 
 * @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;
	}

	/**
	 * 查找或创建一个intern对象。<br/>
	 * 
	 * @param rawObject
	 * @return
	 */
	public final TYPE intern(final TYPE rawObject) {
		// 空值是唯一的,也不需要加线程锁
		if (rawObject == null) {
			return null;
		}
		synchronized (m_normalObjectToInternObject) {
			{// 尝试确定输入是不是本身就是intern。我认为找到intern后,程序中会一直使用intern,而源数据很可能被抛弃。所以输入是intern的概率较大。
				if (m_internObjectStorage.containsKey(rawObject)) {
					return rawObject;
				}
			}
			{// 尝试查找可能的旧intern对象。这个源对象有可能是之前处理过的。
				TYPE internObject = m_normalObjectToInternObject.get(rawObject);
				// 已经有一个intern对象
				if (internObject != null) {
					return internObject;
				}
			}
			// 现在可以确定输入不可能是intern,也没有历史记录。随后将进行复杂的计算。
			// 尝试在intern仓库中查找可用的旧intern对象。
			for (Entry<TYPE, Object> objectInInternSet : m_internObjectStorage.entrySet()) {
				TYPE internObject = objectInInternSet.getKey();
				boolean comp = compare(rawObject, internObject);
				// 相同文件共享intern对象
				if (comp) {
					// 存入映射清单,加速下一次的匹配
					m_normalObjectToInternObject.put(rawObject, internObject);
					return internObject;
				}
			}
			{ // 创建新intern对象,并放入intern仓库。
				TYPE internObject = m_internCreator.apply(rawObject);
				m_internObjectStorage.put(internObject, null);
				// intern创建函数有可能会返回源对象。不能把用于intern的对象放入映射表
				if (rawObject != internObject) {
					// 存入映射清单,加速下一次的匹配
					m_normalObjectToInternObject.put(rawObject, internObject);
				}
				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);
	}

	@FunctionalInterface
	public 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对象,也可以复制一个。

现在贴一个对File的实现:

package pers.laserpen.util.file;

import java.io.File;

import com.sun.jna.Platform;

import pers.laserpen.util.java.WeakIntern;

/**
 * File对象唯一引用,用于文件对比和文件操作的线程锁。如果同一进程有两个线程需要文件锁,那么需要先加线程锁。
 * 
 * @author Laserpen
 */
public final class WeakFileIntern extends WeakIntern<File> {

	public WeakFileIntern() {
		super((a, b) -> 0 == compareFile(a, b), file -> new File(StaticFileUtils.tryGetCanonicalPath(file)));
	}

	private static int compareFile(final File a, final File b) {
		if (a == b) {
			return 0;
		}
		if (a == null) {
			return -1;
		}
		if (b == null) {
			return 1;
		}
		String fileAString = StaticFileUtils.tryGetCanonicalPath(a);
		int fileALength = fileAString.length();

		String fileBString = StaticFileUtils.tryGetCanonicalPath(b);
		int fileBLength = fileBString.length();

		if (Platform.isWindows() || Platform.isWindowsCE()) {
			fileAString = fileAString.toLowerCase();
			fileBString = fileBString.toLowerCase();
		}

		int minLength = Math.min(fileALength, fileBLength);
		for (int i = 0; i < minLength; ++i) {
			char ca = fileAString.charAt(i);
			char cb = fileBString.charAt(i);
			int delta = ca - cb;
			if (delta != 0) {
				return delta;
			}
		}
		return fileALength - fileBLength;

	}

}

这个已经不是核心代码,关联代码就不继续扩展了,作为参考,不能运行。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值