你好,最近在一次面试中,我被问到有一个Hashmap、一个ArrayList和一个Hashset。它们各自包含相同的10个用户定义对象(例如:Employee类对象)。哪个会占用更多堆空间,为什么?
我回答说Hashmap会占用更多的空间,因为它存储键-值对。但是Hashset内部也使用Hashmap来存储值。
- 请问有人能给出带有原因的答案吗?
- 是否有任何工具或Eclipse插件可以让我自己检查这个问题?
谢谢。
你好,最近在一次面试中,我被问到有一个Hashmap、一个ArrayList和一个Hashset。它们各自包含相同的10个用户定义对象(例如:Employee类对象)。哪个会占用更多堆空间,为什么?
我回答说Hashmap会占用更多的空间,因为它存储键-值对。但是Hashset内部也使用Hashmap来存储值。
谢谢。
ArrayList
的默认容量为10,因此所有元素都适合其中,HashMap
的默认容量为16,负载因子为0.75,因此在增加容量之前它将接受多达12个元素。当然,在这种情况下(以及其他任何情况下),16
仍然比10
大,因此HashMap
/HashSet
在这种情况下具有更大的容量(并且在需要时HashMap
会将容量加倍,而ArrayList
使用1.5的因子)。 - Holger我发现这很有趣,虽然我同意Eran的观点,但需要适当的证明。我正在使用JOL进行测量。
为了举例说明,我创建了一个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)
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
更改为TreeNode
,差异相当显著,请在此处阅读更多信息。一个Node
是32字节,而一个TreeNode
是56字节。HashSet
和HashMap
都是368字节的结果不是巧合吗?毕竟,你的HashMap
示例有两个额外的Integer
实例,但另一方面,HashSet
示例有额外的private transient HashMap<E,Object> map;
引用(以及同时拥有HashSet
和HashMap
实例的开销)。如果你添加/放置10个条目/元素,你会得到相同的结果吗? - Eran