将大量键映射到少量值

时间:2019-02-03 19:11:22

标签: java algorithm hashmap

如果您有1,000,000个键(整数)映射到10,000个值(整数)。什么是最有效的实现方式(查找性能和内存使用情况)。

假设值是随机的。即没有映射到单个值的键范围。

我能想到的最简单的方法是HashMap,但想知道是否可以通过对与单个值匹配的键进行分组来做得更好。

Map<Integer,Integer> largeMap = Maps.newHashMap();
largeMap.put(1,4);
largeMap.put(2,232);
...
largeMap.put(1000000, 4);

3 个答案:

答案 0 :(得分:3)

如果已知键集在给定范围内(如示例中所示的1-1000000),则最简单的方法是使用数组。问题是您需要按键查找值,这将您限制为映射或数组。

下面使用值到值的映射只是为了避免等值对象的重复实例(这样做可能是更好的方法,但我想不到)。该数组仅用于按索引查找值:

private static void addToArray(Integer[] array, int key, 
        Integer value, Map<Integer, Integer> map) {

    array[key] = map.putIfAbsent(value, value);
}

然后可以使用以下方法添加值:

Map<Integer, Integer> keys = new HashMap<>();
Integer[] largeArray = new Integer[1000001];

addToArray(largeArray, 1, 4, keys);
addToArray(largeArray, 2, 232, keys);
...
addToArray(largeArray, 1000000, 4, keys);

如果new Integer[1000001]似乎很hack,您仍然可以维护某种“索引偏移量”以指示与数组中的索引0关联的实际键。


我将其放在一个类中:

class LargeMap {

    private Map<Integer, Integer> keys = new HashMap<>();
    private Integer[] keyArray;

    public LargeMap(int size) {
        this.keyArray = new Integer[size];
    }

    public void put(int key, Integer value) {
        this.keyArray[key] = this.keys.putIfAbsent(value, value);
    }

    public Integer get(int key) {
        return this.keyArray[key];
    }
}

并且:

public static void main(String[] args) {
    LargeMap myMap = new LargeMap(1000_000);

    myMap.put(1, 4);
    myMap.put(2, 232);
    myMap.put(1000_000, 4);
}

答案 1 :(得分:1)

我不确定您是否可以通过对所有内容进行分组来在此处进行很多优化。如果您想通过值而不是通过键进行查找(即获取具有特定值的所有键),那么“反向”映射可能会为您提供更好的性能,但是由于您没有明确表示要这样做,因此我不会不要采用这种方法。

对于优化,如果键在固定范围内,则可以使用int数组而不是映射。数组查找为O(1),原始数组使用的内存少于映射。

int offset = -1;
int[] values = new int[1000000];
values[1 + offset] = 4;
values[2 + offset] = 232;
// ...
values[1000000 + offset] = 4;

如果范围不是从1开始,则可以调整偏移量。

还有类似trove4j之类的库,它比标准集合为此类数据提供更好的性能和更有效的存储,尽管我不知道它们与简单数组方法相比如何。

答案 2 :(得分:-1)

HashMap是最糟糕的解决方案。整数的散列本身就是散列。如果您想要一个易于使用的解决方案,我会说一个TreeMap。您可以编写自己的专用树图,例如将键分为两个快捷方式,并在树图中包含一个树图。