一个简单的笨重Map,内存中保存一些对象,其他的保存到硬盘。

今天观看论坛,发现有些初学者,总是将大量的数据存入HashMap中,造成内存溢出。

问题的关键是,造成溢出以后,不去改进算法,而是机械的调节JVM的最大内存容量,这并不能从根本上解决问题。

除了B+Tree等较好的算法以外,我想来想去,还有一个相对简单点的实现方案,就是效率硬盘没有索引,效率相对较低。

代码如下:

import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.HashSet;
import java.util.LinkedHashMap;

public class HeavyMap extends LinkedHashMap<String, Integer> {
	private static final long serialVersionUID = -858815745597381386L;
	public static final int DEFAULT_CAPACITY = 2000;
	public static final String File_Prefix = "heavy_map";
	public static final String File_Suffix = "bin";
	private int capacity;// 内存对象容量
	private RandomAccessFile switchFile;// 数据交换文件
	private HashSet<Integer> virtualCache = new HashSet<Integer>();

	public HeavyMap() throws IOException {
		this(DEFAULT_CAPACITY);
	}

	public HeavyMap(int capacity) throws IOException {
		this(capacity, File.createTempFile(File_Prefix, File_Suffix));
	}

	public HeavyMap(int capacity, File switchFile) throws IOException {
		super(16, 0.75f, true);
		this.capacity = capacity;
		if (switchFile.isDirectory()) {
			switchFile = File.createTempFile(File_Prefix, File_Suffix,
					switchFile);
		}
		this.switchFile = new RandomAccessFile(switchFile, "rw");
	}

	@Override
	public Integer put(String key, Integer value) {
		virtualCache.add(key.hashCode());
		return super.put(key, value);
	}

	@Override
	protected boolean removeEldestEntry(
			java.util.Map.Entry<String, Integer> eldest) {
		if (size() >= capacity) {
			updateEntry(eldest);
			return true;
		}
		return false;
	}

	@Override
	public Integer get(Object key) {
		Integer value = super.get(key);
		if (value == null && virtualCache.contains(key.hashCode())) {
			Integer v = findValue((String) key);
			if (v != null) {
				put((String) key, v);
			}
		}
		return value;
	}

	private Integer findValue(Object key) {
		synchronized (switchFile) {
			try {
				switchFile.seek(0L);
				long length = switchFile.length();
				while (switchFile.getFilePointer() < length) {
					int hash = switchFile.readInt();
					int len = switchFile.readInt();
					if (hash == key.hashCode()) {
						byte[] data = new byte[len - 4];
						if (switchFile.read(data) < 0) {
							throw new EOFException();
						}
						int valueNumber = switchFile.readInt();
						String keyString = new String(data, "UTF-8");
						if (keyString.equals(key)) {
							return valueNumber;
						}
					} else {
						switchFile.skipBytes(len);
					}
				}
			} catch (EOFException e) {
				return null;
			} catch (IOException e) {
				throw new IllegalStateException("读取交换区数据时产生异常", e);
			}
		}
		return null;
	}

	private void updateEntry(java.util.Map.Entry<String, Integer> e) {
		synchronized (switchFile) {
			try {
				switchFile.seek(0L);
				long length = switchFile.length();
				while (switchFile.getFilePointer() < length) {
					int hash = switchFile.readInt();
					int len = switchFile.readInt();
					if (hash == e.getKey().hashCode()) {
						byte[] data = new byte[len - 4];
						if (switchFile.read(data) < 0) {
							throw new EOFException();
						}
						int valueNumber = switchFile.readInt();
						String keyString = new String(data, "UTF-8");
						if (keyString.equals(e.getKey())
								&& valueNumber != e.getValue()) {
							switchFile.seek(switchFile.getFilePointer() - 4);
							switchFile.writeInt(e.getValue());
						}
					} else {
						switchFile.skipBytes(len);
					}
				}
			} catch (IOException ex) {
				throw new IllegalStateException("读取交换区数据时产生异常", ex);
			}
		}
	}

	@Override
	public void clear() {
		super.clear();
		virtualCache.clear();
	}
	
	@Override
	protected void finalize() throws Throwable {
		super.finalize();
		switchFile.close();
	}
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值