【问题标题】:Mapping large set of Keys to a small set of Values将大量键映射到一小组值
【发布时间】:2019-06-27 15:01:51
【问题描述】:

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

假设这些值是随机的。即没有一系列键映射到单个值。

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

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

【问题讨论】:

  • 密钥是否已知为1 - 1000000
  • 是的,如果有帮助,可以知道键在固定范围内。

标签: java algorithm hashmap


【解决方案1】:

如果已知键集在给定范围内(如示例中所示的 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] 看起来像个黑客,您仍然可以维护一种“索引偏移”来指示与数组中索引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);
}

【讨论】:

  • Map map 的key===value 为你提供什么。
  • @Chris 我只使用它来避免多次实例化equal 值(请参阅map.putIfAbsent(value, value) 调用)。正如答案中所述,我可能还没有找到更好的方法。
  • 我不明白。如果您将put 实现更改为this.keyArray[key] = value;,那么结果只是一个数组的包装器(无论如何,这在许多方面可能是这里的“最佳”解决方案)。对于Integer,谈论“inantiating equal values”似乎没有意义。
  • @Marco13 原因如下:我想保留 最多 10000 个实例/对象 的可能值。如果你调用put(999999, 12345)12345 会自动装箱到一个新的Integer 对象中。好的...但是再次调用put(999998, 12345) 将导致另一个12345 整数对象。这对于Integer 来说可能微不足道,但我想限制值实例的数量。换句话说,我对12345 拥有完全相同的对象,无论地图将其作为值。这就是this.keys.putIfAbsent 的帮助)
【解决方案2】:

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

如果键在固定范围内,您可以使用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 这样的库,它们为这类数据提供比标准集合更好的性能和更有效的存储,但我不知道它们与简单数组方法相比如何。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-07
      • 2017-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-08
      • 2021-09-11
      • 1970-01-01
      相关资源
      最近更新 更多