当前位置: 首页 > 面试题库 >

Java HashMap与ArrayList相比的内存开销

盖和洽
2023-03-14
问题内容

我想知道与ArrayList相比,Java HashMap的内存开销是多少?

更新:

我想提高搜索相同对象的大包装(600万以上)的特定值的速度。

因此,我正在考虑使用一个或多个HashMap而不是使用ArrayList。但是我想知道HashMap的开销是多少。

据我了解,密钥不是存储的,只是密钥的哈希,因此它应该类似于 对象的哈希大小+一个指针

但是使用什么哈希函数?是对象提供的还是另一种?


问题答案:

如果您将HashMap与ArrayList进行比较,我假设您正在对ArrayList进行某种搜索/索引,例如二进制搜索或自定义哈希表…?因为使用线性搜索无法通过600万个.get(key)项。

使用该假设,我进行了一些实证测试,得出的结论是:“如果将ArrayList与二进制搜索或自定义哈希映射实现结合使用,则与HashMap相比,在相同数量的RAM中可以存储2.5倍的小对象”。
。我的测试基于仅包含3个字段的小对象,其中一个是键,而键是整数。我使用的是32位的jdk 1.6。有关此数字“ 2.5”的注意事项,请参见下文。

要注意的关键事项是:

(a)杀死您的不是引用或“负载因子”所需的空间,而是对象创建所需的开销。如果密钥是原始类型,或者是两个或多个原始或引用值的组合,则每个密钥将需要其自己的对象,该对象带有8个字节的开销。

(b)根据我的经验,您通常需要将键作为值的一部分(例如,存储按客户ID索引的客户记录,但您仍希望将客户ID作为客户对象的一部分)。这意味着IMO在某种程度上浪费了HashMap单独存储对键和值的引用。

注意事项:

  1. 用于HashMap键的最常见类型是String。对象创建开销在这里不适用,因此差异会较小。

  2. 我得到的数字为2.8,与在-Xmx256M JVM上的HashMap中插入3880004相比,将8880502条目插入到ArrayList中,但我的ArrayList负载率为80%,并且我的对象很小-12字节加上8字节对象开销。

  3. 我的图和实现要求键包含在值中,否则我在对象创建开销上会遇到同样的问题,而这只是HashMap的另一种实现。

我的代码:

public class Payload {
    int key,b,c;
    Payload(int _key) { key = _key; }
}


import org.junit.Test;

import java.util.HashMap;
import java.util.Map;


public class Overhead {
    @Test
    public void useHashMap()
    {
        int i=0;
        try {
            Map<Integer, Payload> map = new HashMap<Integer, Payload>();
            for (i=0; i < 4000000; i++) {
                int key = (int)(Math.random() * Integer.MAX_VALUE);
                map.put(key, new Payload(key));
            }
        }
        catch (OutOfMemoryError e) {
            System.out.println("Got up to: " + i);
        }
    }

    @Test
    public void useArrayList()
    {
        int i=0;
        try {
            ArrayListMap map = new ArrayListMap();
            for (i=0; i < 9000000; i++) {
                int key = (int)(Math.random() * Integer.MAX_VALUE);
                map.put(key, new Payload(key));
            }
        }
        catch (OutOfMemoryError e) {
            System.out.println("Got up to: " + i);
        }
    }
}


import java.util.ArrayList;


public class ArrayListMap {
    private ArrayList<Payload> map = new ArrayList<Payload>();
    private int[] primes = new int[128];

    static boolean isPrime(int n)
    {
        for (int i=(int)Math.sqrt(n); i >= 2; i--) {
            if (n % i == 0)
                return false;
        }
        return true;
    }

    ArrayListMap()
    {
        for (int i=0; i < 11000000; i++)    // this is clumsy, I admit
            map.add(null);
        int n=31;
        for (int i=0; i < 128; i++) {
            while (! isPrime(n))
                n+=2;
            primes[i] = n;
            n += 2;
        }
        System.out.println("Capacity = " + map.size());
    }

    public void put(int key, Payload value)
    {
        int hash = key % map.size();
        int hash2 = primes[key % primes.length];
        if (hash < 0)
            hash += map.size();
        do {
            if (map.get(hash) == null) {
                map.set(hash, value);
                return;
            }
            hash += hash2;
            if (hash >= map.size())
                hash -= map.size();
        } while (true);
    }

    public Payload get(int key)
    {
        int hash = key % map.size();
        int hash2 = primes[key % primes.length];
        if (hash < 0)
            hash += map.size();
        do {
            Payload payload = map.get(hash);
            if (payload == null)
                return null;
            if (payload.key == key)
                return payload;
            hash += hash2;
            if (hash >= map.size())
                hash -= map.size();
        } while (true);
    }
}


 类似资料:
  • Epoll 是 Linux 内核在2.5.44版本引进的一个新特性,旨在替换之前系统中老的 select, poll 等系统请求。这是 Linux I/O 系统一次质的飞跃。关于 Epoll 的详细的介绍见 Wikipedia。 Epoll 在绝大多数情况下性能都远超 select 或者 poll,但是除了速度之外,三者之间的 CPU 开销,内存消耗情况又怎么样呢? 本文的内容来自 Stackov

  • 我了解到是作为双链表实现的,它在添加和删除上的性能比好,但在get和set方法上的性能更差。 这是否意味着我应该选择而不是来插入? 我写了一个小测试,发现插入速度更快,那么链表怎么比快呢? 请参考下面我所做的例子。

  • 问题内容: 在各种情况下,我观​​察到C ++中的链表迭代始终比Go中的慢10-15%。我在解决堆栈溢出这个神秘的第一次尝试是在这里。我编写的示例有问题,因为: 1)由于堆分配,内存访问是不可预测的,并且 2)因为没有实际的工作要做,所以一些人的编译器正在优化主循环。 为了解决这些问题,我有一个新程序,其中包含C 和Go的实现。C 版本花费1.75秒,而Go版本花费1.48秒。这次,我在计时开始之

  • 问题内容: 我需要存储大量信息,例如在Java List中存储“名称”。项目的数量可以更改(或者简而言之,我无法预定义大小)。我认为从内存分配的角度来看,LinkedList比ArrayList更好,对于ArrayList,一旦达到最大大小,内存分配将自动加倍,因此总有可能分配比需要什么。 我从这里的其他文章中了解到,存储在LinkedList中的各个元素比ArrayList占用更多的空间,因为L

  • JavaScript 有两种方式判断两个值是否相等。 等于操作符 等于操作符由两个等号组成:== JavaScript 是弱类型语言,这就意味着,等于操作符会为了比较两个值而进行强制类型转换。 "" == "0" // false 0 == "" // true 0 == "0"

  • 问题内容: Dalvik的内存模型与Java的相同吗?我对引用和非/非原始变量的读写是否是原子的特别感兴趣,但是我也想知道两个平台的内存模型之间是否存在差异。 问题答案: 从4.0(冰淇淋三明治)开始,Dalvik的行为应与JSR-133(Java内存模型)相匹配。 从3.0(Honeycomb)开始,大多数组件都已安装到位,但忽略了一些在实践中很难遇到的小事情(例如,最终确定中的一些边缘案例)。