ArrayList vs HashSet,哪个对内存性能更好?

ArrayList vs HashSet, which is better for memory performance?

在下面的测试中:

public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("start 1: " + new Date());
                Collection collection = new ArrayList();
                for (int i = 0; i < 1000 * 1000 * 1000; i++) {
                    collection.add(new Object());
                }
                System.out.println("middle 1: " + new Date());
                Iterator iterator = collection.iterator();
                while (iterator.hasNext()) {
                    iterator.next().toString();
                }
                System.out.println("\nend 1: " + new Date());
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("start 2: " + new Date());
                Collection collection = new HashSet();
                for (int i = 0; i < 1000 * 1000 * 1000; i++) {
                    collection.add(new Object());
                }
                System.out.println("middle 2: " + new Date());
                Iterator iterator = collection.iterator();
                while (iterator.hasNext()) {
                    iterator.next().toString();
                }
                System.out.println("\nend 2: " + new Date());
            }
        });
        t1.start();
        t2.start();
    }

我遇到了 ArrayList 方法的 OutOfMemory。

但是,on this high voted answer,它说 HashSet 消耗更多内存。 答案中损坏的 link 看起来像是 this one.

我的测试有问题吗?或者 ArrayLists 在内存使用方面真的比 HashSet 更差?

--更新:实际上,通过使用 collection.add(".");我从 arrayList 中获取 outOfMemory。但是对于 collection.add(new Object());我两个都买。

哈希集消耗更多内存是有意义的,因为它们需要为潜在的哈希值(桶)设置空间。现在我不知道它是否消耗了 5.5 倍的内存(当然这取决于您存储的对象的性质)但可以这样想。哈希集之于哈希表就像数组列表之于数组一样。您将在哈希集中有一些空白空间,这就是消耗额外内存的地方,但是对于您正在进行的 10 亿次迭代,这可能是值得的

当你想测量堆中对象的大小时,一个好的方法是使用优秀的工具SizeOf,由于方法,它可以让你获得对象的深度大小SizeOf.deepSizeOf

所以这里例如一个 ArrayList 只有 1000 万个对象实例(实际上我没有像你的例子那样使用 10 亿)需要 205 Mo ,如果我将大小正确设置为 1000 万(感谢 new ArrayList(size)),它会将大小减小到 190 Mo。在 HashSet 的情况下,无论我是否设置大小,它都需要 521 Mo 所以它要大两倍以上。

注意: 我在 64-Bit JVM 启用 UseCompressedOops 的情况下得到了这些结果。

请注意,大小将取决于目标 JVM,如果目标 JVM 是 32-bit JVM64-bit JVMUseCompressedOops (-XX: +UseCompressedOops) 启用,默认情况下堆小于 32 GB64-bit JVM 禁用 UseCompressedOops (-XX:-UseCompressedOops).