我正在存储一个映射,我有seq
个整数作为键。 hashmap非常大(100,000个键),所以我想要最紧凑的存储方法来节省RAM。
有一些选项,包括Clojure LazySeq
s,向量,java.util.Arrays
甚至是字符串。我不需要上述的懒惰,持久性或实习,只是将它们用作不透明的键。
是否已知此类数据的最小最小代表?
编辑,因为问题并不清楚我想使用哈希映射,因为它提供的功能。我不想要另一种抽象数据类型。我有兴趣尽量减少键的大小。
答案 0 :(得分:2)
如果内存优化很重要,您可能希望使用int(或long)的Java本机数组。但是,您需要定义一个包装器类,以便完成正确的equals
和hashcode
合同,因为Java本机数组是对象,但只是继承equals
和hashCode
Objects
。
我还没有采取任何措施,但是 - 根据密钥序列中的整数数量 - 并且知道不可变Clojure向量背后的数据结构,这可能会产生重大影响。
要执行本机数组键,可以使用java.lang.Arrays
中的实用程序函数,并在Java中定义一个简单的包装器:
public final class IntKey {
private final int[] data;
public IntKey(int[] data) {
if (data == null) {
throw new NullPointerException();
}
this.data = data;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof IntKey)) {
return false;
}
return Arrays.equals(data, ((IntKey)other).data);
}
@Override
public int hashCode() {
return Arrays.hashCode(data);
}
}
或者使用deftype
在Clojure中执行此操作:
(deftype IntKey [^ints data]
java.lang.Object
(equals [this other]
(java.util.Arrays/equals data (.data other)))
(hashCode [this]
(java.util.Arrays/hashCode data)))
然后测试原生数组不是好键:
(def k1 (int-array [1 2 3]))
(def k2 (int-array [4 5 6]))
(def k3 (int-array [1 2 3])) ;; same sequence as k1
(def h (hash-map ik1 "hello" ik2 "good" ik3 "bye"))
user> (map h [ik1 ik2 ik3])
user> ("hello" "good" "bye") ;; argh ik1 and ik3 should yield the same value
在IntKey
中包装int数组并重新定义地图:
(def ik1 (IntKey. k1))
(def ik2 (IntKey. k2))
(def ik3 (IntKey. k3))
(def h (hash-map ik1 "hello" ik2 "good" ik3 "bye"))
user> (map h [ik1 ik2 ik3])
("bye" "good" "bye") ;; ok
user> (count h)
2
注意:您可能希望"缓存"包装器类中的哈希码值,每个序列只计算一次。
答案 1 :(得分:0)
这实际上取决于您需要对此数据执行的操作。最常见的解决方案是[[set,value],...],尽管它并不比hashmap好得多。可能是时候转向外部K / V了吗?
答案 2 :(得分:0)
如前所述,这实际上取决于您需要对数据执行的操作。
例如,如果您不介意某种程度的误报(但没有假阴性),您可以使用bloom filters,或者,如果键是静态的,您可以使用minimal perfect hash function,你可以用来访问数组或文件。
<强>更新强>
布隆过滤器确实用于表示集合而不是映射。但是,我发现了一篇有趣的论文,概括了bloom filters into maps。虽然没有找到它的任何实现。