有效地遍历散列映射中的所有MATCHING键?

时间:2009-02-11 17:40:26

标签: java search performance iterator hashmap

我有一个HashMap,有数百万个条目。

需要检索其键与特定条件集匹配的所有条目(在这种情况下,每个键都是具有两个整数属性的对象;我需要检索其中每个整数落在指定范围内的所有键)。 / p>

迭代所有这些密钥的最快,最有效的方法是什么?

更新 在这种特殊情况下,尽管我没有预先指定它,但是键中的第一个整数优先于第二个整数。

7 个答案:

答案 0 :(得分:7)

HashMap不是一种有效的数据结构,用于查找位于特定范围内的键。通常,您可以在哈希映射中有效找到的唯一键是具有与您拥有的相同哈希的键(即相等键)。

要查找位于特定范围内的键,最好使用某种SortedMap,例如TreeMap,然后可以使用SortedMap.subMap(低,高)视图方法查看

至于基于两个键找到一个键,这就更难了。你最好的选择是迭代第一个整数范围的subMap,然后检查每个整数是否在指定范围内。这至少将扫描限制为具有该范围内的整数之一的键。尝试根据您可能必须搜索的可能范围内具有更自然的值分布的整数对地图进行排序。

答案 1 :(得分:3)

以下是使用TreeMap解决方案

public static void main(String[] args) {
    Comparator<Foo> fooComparator = new Comparator<Foo>() {
        @Override
        public int compare(Foo o1, Foo o2) {
            return o1.compareTo(o2);
        }
    };

    TreeMap<Foo, String> map = new TreeMap<Foo, String>(fooComparator);

    map.put(new Foo(1, 4), "");
    map.put(new Foo(1, 3), "");
    map.put(new Foo(2, 4), "");
    map.put(new Foo(3, 4), "");
    map.put(new Foo(8, 10), "");
    map.put(new Foo(8, 17), "");
    map.put(new Foo(10, 10), "");

    int a = 2;
    int b = 5;

    for (Foo f : getKeysInRange(map, a, b)) {
        System.out.println(f);
    }
}

public static List<Foo> getKeysInRange(TreeMap<Foo, String> map, int low, int high) {
    Foo key1 = new Foo(low, low);
    Foo key2 = new Foo(high, high);

    Foo fromKey = map.ceilingKey(key1);
    Foo toKey = map.floorKey(key2);

    if (fromKey != null && toKey != null && fromKey.compareTo(toKey) < 0)
        return new ArrayList<Foo>(map.subMap(fromKey, true, toKey, true).keySet());
    return new ArrayList<Foo>();
}

public static class Foo implements Comparable<Foo> {
    private int i;
    private int j;

    private Foo(int i, int j) {
        super();
        this.i = i;
        this.j = j;
    }

    public int min() {
        if (i < j)
            return i;
        else
            return j;
    }

    public int max() {
        if (i > j)
            return i;
        else
            return j;
    }

    @Override
    public String toString() {
        return "I=" + i + "J=" + j;
    }

    @Override
    public int compareTo(Foo o) {
        if (this.min() > o.min()) {
            return 1;
        } else if (this.min() < o.min())
            return -1;
        else {
            if (this.max() > o.max())
                return 1;
            else if (this.max() < o.max())
                return -1;
            else
                return 0;
        }
    }
}

答案 2 :(得分:1)

如果不遍历整个keySet,就无法做到这一点。

如果您确定不会有其他条目具有与这些整数属性相同的值,那么您可以使用具有排序条件的TreeMap,该排序条件将按两个整数属性的某种组合进行排序,然后您就可以直接找到第一个匹配,然后从那里迭代到第一个不匹配。但似乎不太可能达到这些条件。

因为集合的开销非常低(一切都是通过引用存储的),我会考虑制作两个已排序的集合,可能是TreeSet,一个按第一个属性排序,另一个按第二个属性排序,然后选择满足的所有值两个集合的标准并将它们结合在一起。

答案 3 :(得分:1)

bruno conde提供的解决方案是一个良好的开端。然而,我读取原始问题的方式是关键对象包含两个整数,问题是关于检索与第一个整数的一个范围匹配的所有键/值对的最快方法,并匹配第二个范围的第二个范围整数。 bruno解决方案假设键具有自然顺序,其中第一个整数始终优先于第二个整数。它还假设只有一个范围。

对于这个更一般的情况,我会:  使用有利于integer1的比较器将键/值插入TreeMap  使用有利于integer2

的比较器将相同的键/值插入第二个TreeMap

然后,您可以使用范围在每个TreeMap上使用subMap()以获取基础TreeMap的有序视图。然后,您可以基于这些subMaps的keySet()的交集(retainAll())创建新的结果TreeSet。

答案 4 :(得分:0)

可能没有比以下更快的解决方案:

for (final KeyObj key : map.keySet()) {
    // do work
}

答案 5 :(得分:0)

如果某个TreeSet由于某种原因不起作用,则迭代的标准方法是使用条目集。

for (Map.Entry<MyKeyType, MyValueType> entry : myMap.entrySet()) {
    MyKeyType key = entry.getKey();
    if (isValid(key)) {
        // do whatever
        validList.add(entry.getValue());
    }
}

这样,您无需为有效密钥执行额外的myMap.get(key)调用。

答案 6 :(得分:0)

您可能需要考虑某种SQL DB,可能是内存版本,如DerbyH2。这在很大程度上取决于它的重要程度以及它的快速程度。然后你可以在SQL中执行此操作,让引擎完成所有优化工作。