嗨最近在接受采访时,我被问到我有一个Hashmap,一个ArrayList和一个Hashset。它们每个包含相同的10个用户定义对象(例如:Employee类对象)。哪个会占用更多的堆空间?为什么?
我给出了Hashmap的答案,因为它存储了两个键值对。但是Hashset在内部也使用hashmap来存储值。
感谢。
答案 0 :(得分:3)
如果要计算容器所需的内存和" 10用户定义的对象所需的内存"那你是对的。
HashMap
会占用更多空间。
即使HashSet
由HashMap
支持,它在其所有条目中存储的值也是对同一虚拟实例的引用。
因此HashSet
需要10个关键引用+10个值引用+10个元素+ 1个虚拟实例。
另一方面,HashMap
需要10个关键引用+10个值引用+10个键实例+10个值实例(假设" 10个用户定义的对象"存储为值)。
当然,为了更准确,您还必须计算包含HashMap
存储桶的数组的大小,但HashMap
和HashSet
中的数据相同,因为它们包含相同数量的元素。
请注意,正如JB Nizet评论的那样,如果HashMap
的密钥是" 10个用户定义的对象"的属性,那么" 10个密钥实例"不需要额外的内存(因为它们已经存在),因此HashMap
和HashSet
都需要相同数量的内存来存储10个对象,而HashSet
实际上需要一点更多内存,因为它包含对HashMap
的引用。
ArrayList
应该比HashSet
和HashMap
占用更少的内存,因为ArrayList
的后备数组的默认初始长度为10(这足够了)存储10个对象),而HashMap
的存储区数组的默认初始长度为16(也足以存储10个对象,假设我们使用的默认加载因子为0.75)。
答案 1 :(得分:2)
我发现这非常有趣,虽然我在这里同意伊兰,但需要适当的证明。我正在使用JOL for the measuring。
出于示例的目的,我创建了一个Employee
,其中包含两个字段String name
和int age
。
让我们看看发生了什么:
List<Employee> list = new ArrayList<>();
list.add(new Employee(22, "a"));
System.out.println(GraphLayout.parseInstance(list).totalSize()); //152 bytes
让我们看看这个空间的来源:
12 bytes ArrayList headers
4 bytes int modCount in ArrayList
4 bytes int size in ArrayList
4 bytes for the reference "elementData" in ArrayList
12 bytes for the Employee headers
4 bytes int age Employee
4 bytes for String name reference
4 bytes padding (objects are 8 bytes aligned)
12 bytes for the String "a" headers
4 bytes for the char[] reference
4 bytes for the int hash
4 bytes padding
12 bytes for the new char[] { 'a' }
4 bytes the size of the array (store in headers)
2 bytes for char 'a'
6 bytes padding
40 bytes for the 10 references in elementData array
12 bytes for it's headers (arrays are Objects)
4 bytes for the size (arrays have a size)
为了示例,我接下来将添加2名员工,并保持关于尺寸缩短的说明:
HashMap<Employee, Integer> map = new HashMap<>();
map.put(new Employee(22, "a"), 100);
map.put(new Employee(23, "b"), 200);
System.out.println(GraphLayout.parseInstance(map).toFootprint());
你会得到这样的输出:
COUNT AVG SUM DESCRIPTION
2 24 48 [C
1 80 80 [Ljava.util.HashMap$Node;
2 16 32 java.lang.Integer
2 24 48 java.lang.String
1 48 48 java.util.HashMap
2 32 64 java.util.HashMap$Node
2 24 48 org.erabii.tenelemdiff.Test$Employee
12 368 (total)
总大小为368
个字节。现在让我们将它们放入HashSet
:
HashSet<Employee> set = new HashSet<>();
set.add(new Employee(22, "a"));
set.add(new Employee(23, "b"));
System.out.println(GraphLayout.parseInstance(set).totalSize()); // 368 bytes
您可以看到HashSet
和HashMap
的大小与此特定方案相同。如果您添加超过12个条目(默认情况下为HashMap
重新调整大小)并且可能会将其从LinkedNode
更改为{{1},那么事情会变得更加棘手s和差异is quite significant, read more here。 TreeNode
是32个字节,而Node
是56个字节。