java.util.Map和java.util.Set的优化实现?
我正在编写一个应用程序,在这个应用程序中,内存和速度是至关重要的。我从分析中发现,我在映射和设置操作上花费了大量时间。在我研究如何减少对这些方法的调用时,我想知道是否有人编写过或遇到过显著改善访问时间或内存开销的实现?或者至少,在一些假设下,这可以改善这些情况 从JDK源代码来看,我不能相信它不能更快或更精简 我知道Commons集合,但我不相信它有任何目标是更快或更精简的实现。谷歌收藏也是如此java.util.Map和java.util.Set的优化实现?,java,performance,collections,map,set,Java,Performance,Collections,Map,Set,我正在编写一个应用程序,在这个应用程序中,内存和速度是至关重要的。我从分析中发现,我在映射和设置操作上花费了大量时间。在我研究如何减少对这些方法的调用时,我想知道是否有人编写过或遇到过显著改善访问时间或内存开销的实现?或者至少,在一些假设下,这可以改善这些情况 从JDK源代码来看,我不能相信它不能更快或更精简 我知道Commons集合,但我不相信它有任何目标是更快或更精简的实现。谷歌收藏也是如此 更新:应该注意到我不需要线程安全。可能不是Map或Set导致了问题,而是它们背后的对象。根据您的问题
更新:应该注意到我不需要线程安全。可能不是
Map
或Set
导致了问题,而是它们背后的对象。根据您的问题,您可能需要更为数据库类型的方案,其中“对象”存储为一组字节,而不是Java对象。您可以嵌入一个数据库(比如ApacheDerby)或者做自己的专业工作。这完全取决于你实际上在做什么HashMap
并不是故意的大而慢…您可以扩展AbstractMap和/或AbstractSet作为起点。不久前,我这样做是为了实现一个基于二进制trie的映射(键是一个整数,树上的每个“级别”都是一个位位置,左边的子级是0,右边的子级是1)。这对我们来说效果很好,因为密钥是EUI-64标识符,而对我们来说,大多数时候前5个字节都是相同的
要实现抽象映射,您至少需要实现entrySet()方法,以返回一组Map.Entry,每个Map.Entry都是一个键/值对
要实现集合,您需要扩展AbstractSet并提供size()和iterator()的实现
然而,至少是这样。您还需要实现get和put,因为默认映射是不可修改的,get的默认实现会在entrySet中迭代寻找匹配项。您看过吗?网址:
Trove旨在提供java.util.Collections API的快速、轻量级实现
提供基准测试。尝试改进equals和hashCode方法的性能,这有助于加快对象的标准容器使用速度。查看GNU Trove:
Commons Collections有,但我不知道它们有什么价值…Commons Collections中至少有一个实现是专门为速度而构建的:它非常具体,只要不超过3个元素,它就会非常快
我猜想,通过遵循@thaggie的建议,您可能会得到更多的启发。请查看equals/hashcode方法时间。通常,这些方法非常快。 有几件事你应该检查一下:你的哈希代码实现了吗?它们是否足够统一?否则你会得到垃圾的表现
- Commons Collections有一个id映射,它通过==进行比较,这应该更快。
-
as拥有原始集合,而Trove也拥有原始集合。我用Trove做了实验,发现它的内存使用更好[Joda Primities][1]
- 我用几个整数映射了许多小对象的集合。将这些更改为int节省了近一半的内存(尽管需要一些更混乱的应用程序代码来补偿)
- 在我看来,排序树应该比hashmaps占用更少的内存是合理的,因为它们不需要加载因子(尽管如果有人可以确认或者有理由认为这实际上是愚蠢的,请在评论中发表)
- 你说你分析了一些课程,但是你有没有做过任何计时来检查它们的速度?我不知道你会如何检查他们的内存使用情况。在比较不同的实现时,如果手头有一些具体的数据,那就太好了。除了Google和Commons集合之外,我还知道以下这些数据:
当然,您可以始终实现您自己的数据结构,这些数据结构针对您的用例进行了优化。为了更好地提供帮助,我们需要了解您的访问模式以及您在集合中存储的数据类型。这里有一些注释,并链接到几个可选的数据结构库:
我还将对fastutil投下强烈的一票。(在另一个回复中提到,在那一页上)它的数据结构比你所能看到的要多,并且版本针对基本类型(如键或值)进行了优化。(缺点是jar文件很大,但您可以根据需要对其进行修剪)您可以通过以下方式节省一点内存: (a) 使用更强、更宽的哈希代码,从而避免存储密钥 (b) 通过从数组中分配自己,避免为每个哈希表条目创建单独的对象 如果它有用的话,这里有一个我有时觉得有用的数字倒数哈希表的简单Java实现。您可以直接在CharSequence(包括字符串)上设置关键帧,否则您必须自己为对象提供一个强大的ish 64位哈希函数 请记住,此实现不存储键,因此,如果两个项具有相同的哈希代码(按2^32的顺序进行哈希运算后会出现这种情况,如果具有良好的哈希函数,则会出现几十亿个项),则一个项将覆盖另一个项:
public class CompactMap<E> implements Serializable {
static final long serialVersionUID = 1L;
private static final int MAX_HASH_TABLE_SIZE = 1 << 24;
private static final int MAX_HASH_TABLE_SIZE_WITH_FILL_FACTOR = 1 << 20;
private static final long[] byteTable;
private static final long HSTART = 0xBB40E64DA205B064L;
private static final long HMULT = 7664345821815920749L;
static {
byteTable = new long[256];
long h = 0x544B2FBACAAF1684L;
for (int i = 0; i < 256; i++) {
for (int j = 0; j < 31; j++) {
h = (h >>> 7) ^ h;
h = (h << 11) ^ h;
h = (h >>> 10) ^ h;
}
byteTable[i] = h;
}
}
private int maxValues;
private int[] table;
private int[] nextPtrs;
private long[] hashValues;
private E[] elements;
private int nextHashValuePos;
private int hashMask;
private int size;
@SuppressWarnings("unchecked")
public CompactMap(int maxElements) {
int sz = 128;
int desiredTableSize = maxElements;
if (desiredTableSize < MAX_HASH_TABLE_SIZE_WITH_FILL_FACTOR) {
desiredTableSize = desiredTableSize * 4 / 3;
}
desiredTableSize = Math.min(desiredTableSize, MAX_HASH_TABLE_SIZE);
while (sz < desiredTableSize) {
sz <<= 1;
}
this.maxValues = maxElements;
this.table = new int[sz];
this.nextPtrs = new int[maxValues];
this.hashValues = new long[maxValues];
this.elements = (E[]) new Object[sz];
Arrays.fill(table, -1);
this.hashMask = sz-1;
}
public int size() {
return size;
}
public E put(CharSequence key, E val) {
return put(hash(key), val);
}
public E put(long hash, E val) {
int hc = (int) hash & hashMask;
int[] table = this.table;
int k = table[hc];
if (k != -1) {
int lastk;
do {
if (hashValues[k] == hash) {
E old = elements[k];
elements[k] = val;
return old;
}
lastk = k;
k = nextPtrs[k];
} while (k != -1);
k = nextHashValuePos++;
nextPtrs[lastk] = k;
} else {
k = nextHashValuePos++;
table[hc] = k;
}
if (k >= maxValues) {
throw new IllegalStateException("Hash table full (size " + size + ", k " + k);
}
hashValues[k] = hash;
nextPtrs[k] = -1;
elements[k] = val;
size++;
return null;
}
public E get(long hash) {
int hc = (int) hash & hashMask;
int[] table = this.table;
int k = table[hc];
if (k != -1) {
do {
if (hashValues[k] == hash) {
return elements[k];
}
k = nextPtrs[k];
} while (k != -1);
}
return null;
}
public E get(CharSequence hash) {
return get(hash(hash));
}
public static long hash(CharSequence cs) {
if (cs == null) return 1L;
long h = HSTART;
final long hmult = HMULT;
final long[] ht = byteTable;
for (int i = cs.length()-1; i >= 0; i--) {
char ch = cs.charAt(i);
h = (h * hmult) ^ ht[ch & 0xff];
h = (h * hmult) ^ ht[(ch >>> 8) & 0xff];
}
return h;
}
}
公共类CompactMap实现可序列化{
静态最终长SerialVersionId=1L;
私有静态最终int MAX_HASH_TABLE_SIZE=1>7)^h;
h=(h>>10)^h;
}
byteTable[i]=h;
}
}
私有整数最大值;
私有int[]表;
脉波重复间隔