我有一个处理大量数据集的程序。这些对象最好存储在散列实现的容器中,因为程序一直在寻找容器中的对象。
第一个想法是使用HashMap,因为获取和删除此容器的方法更适合我需要的用途。
但是,我开始看到HashMap的使用非常耗费内存,这是一个主要问题,所以我认为切换到HashSet会更好,因为它只使用<E>
,而不是<K,V>
每个元素,但当我看到我学到的实现它使用底层的HashMap!这意味着它不会节省任何记忆!
所以这是我的问题:
是否还有其他基于哈希的容器会显着减少内存耗材?
更新
根据评论中的要求,我将对我的程序进行一些扩展,hashMap意味着保存一对其他对象,以及一些数值 - 从它们计算的浮点数。一路上它抽出一些并进入新的对。给定一对,它需要确保它不会保持这对或删除它。可以使用浮点值或对象对象的hashCode
来完成映射。
此外,当我说&#34;庞大的数据集&#34;我说的是~4 * 10 ^ 9个对象
答案 0 :(得分:13)
有关this site关于java中集合性能的非常有用的提示。
HashSet
建立在HashMap< T, Object >
之上,其中值为a 单身“现在”的对象。这意味着the memory consumption of aHashSet is identical to HashMap
:为了存储SIZE
值,您需要 32 * SIZE + 4 * CAPACITY 字节(加上值的大小)。它绝对不是一个对记忆友好的系列。THashSet可能是
HashSet
最简单的替换集合 - 它实现了Set和Iterable,这意味着您应该只在集合的初始化中更新单个字母。
THashSet
使用单个对象数组作为其值,因此它使用 4 * CAPACITY 字节进行存储。如您所见,与JDK HashSet相比,在相同的加载因子的情况下,您将保存 32 * SIZE 字节,这是一个巨大的改进。
此外,我从here拍摄的图片可以帮助我们记住选择合适的收藏品
答案 1 :(得分:5)
我所有的假设都是真的吗?
使用HashSet
实现HashMap
是正确的,因此您不会使用HashSet
来保存任何内存。
如果您要创建包含大量元素的地图,则应根据您的知识构建HashMap
Entry
,以防止反复重复(从而记忆颠簸)。
HashMap内存是否浪费?更具体地说,它的开销是多少 每个条目?
不,这不浪费。开销是基础数组(由initialCapacity
修改的大小),以及每个键值对的0.75
对象。除了存储键和值之外,入口对象还存储指向槽中下一个条目的指针(如果两个或多个条目占据底层阵列中的相同槽)。默认的loadFactor HashSet
使基础数组大小保持在条目数的133%。
非常具体地说,每个条目的内存开销是:
很难获得比基于散列的集合更多的修剪。
HashSet和HashMap一样浪费吗?
使用HashMap
代替int
,您将无法获得内存效率。
是否有任何其他基于哈希的容器将是显着的 更少的内存消耗品?
如果你的密钥是基元(例如Map
s),那里有自定义的Set
和{{1}}实现(在loadFactor
中),它们使用更多内存有效的数据结构。
答案 2 :(得分:1)
HashSet确实使用与HashMap一样多的内存。 HasSet实现Set的两者之间的差异,即,它不关心与键相关联的任何值,仅关注特定值的存在与否。 HashMap关注的是每个键存储/检索(put / get)值。
虽然HashMap / HashSet将数据存储在一个通常比元素数量稍大的数组中,但这并不是一个问题,因为负载因子是0.75。这意味着当元素数量达到底层数组大小的75%时,HashMap将会增长。
比大地图更大的问题是许多空地图,因为HashMap的默认大小是16.这可以通过将初始容量设置为0来抵消。
然而,您也可以使用TreeMap,因为TreeMap基于引用而不是数组,您可能会浪费更多空间,尤其是对于较大的地图,除了也会失去一些速度。 TreeMap的主要好处是它将键保持在有序状态,所以如果你需要它们,那么这就是要走的路。
此外,当您不能或不想自定义实现密钥类型的equals
和hashCode
方法时,可以将TreeMap用于编程原因。您可以为密钥类型制作比较器。例如,要基于不区分大小写的String创建映射/集,请使用String.CASE_INSENSITIVE_ORDER
作为TreeSet的比较器