【问题标题】:Efficient Way To Pull ith Value from Java HashMap?从 Java HashMap 中提取价值的有效方法?
【发布时间】:2016-11-30 04:53:44
【问题描述】:

Java 人你好,

我正在学习 Java HashMap。虽然我喜欢将它们放在一起是多么容易,但我正在考虑以一种有效的方式从地图中提取第 i 个条目。解释...

假设这是我的代码:

package HashPackage;

import java.util.HashMap;

public class newHashObject {

    // Nested class
    public class newObject {
        int Data1;
        int Data2;
        public newObject(int a, int b){
            this.Data1 = a;
            this.Data2 = b;
        }
    }

    // HashMap to contain newObjects
    HashMap<Integer, newObject> cache = new HashMap<Integer, newObject>();

    // Constructor
    public newHashObject(){
        // populate cache with 1000 newObjects:
        for(int i=0; i<1000; i++)
            cache.put(i, new newObject(i, i*2+101));
        System.out.println("New cache created, total objects in cache: "+cache.size());
    }
}

好的,到目前为止还没有什么激进的。在现实生活中,我的 HashMap 中的条目不会使用等于 0、1、2、3 等的键来归档,而是会使用本质上是随机数的键来归档。即,如果您要检查我的“现实生活”HashMap,您会看到键为 19、79、235、577、1023、1092 等的条目。

现在假设我需要从 HashMap 中准确提取第 i 个元素。我不会提前知道键值。例如,使用上面的“现实生活”地图:如果我们开始用 0 对地图的条目进行编号,并且我想取出第 i=4 个条目,那么我应该得到键为 1023 的条目。

我已经考虑过了,我想我可以从 0 到 i 遍历我的 HashMap:

    import java.util.Iterator;
    ...
    // Is there a better way to do this?
    public newObject iterateByIndex(int index){
        Iterator<Integer> keySetIterator = cache.keySet().iterator();
        int count=0;
        if(index<cache.size()){
            while(keySetIterator.hasNext()){
                Integer key = keySetIterator.next();
                if(count==index){
                    // We've found the ith entry in the cache
                    return cache.get(key);
                }
                count++;
            }
        }
        return null;
    }   

这段代码有效,但看起来很笨拙,而且效率肯定很低。我可能需要调用此方法数百万次(不撒谎!),并且每次从 0 迭代到 i 将耗费大量时间。

那么……有什么建议吗? HashMap 在这里是错误的数据结构吗? (我使用 HashMap 是因为我的数据集非常非常大。)我很好奇经验丰富的程序员在这种情况下会做什么。

感谢您的任何建议, -P

【问题讨论】:

  • 如果您需要在 HashMap 中的“ith”元素处引用某些内容,则说明您使用了错误的数据结构。
  • HashMap 没有定义的顺序,所以“i-th”不是一个定义的概念。 “这个类不保证地图的顺序;特别是,它不保证顺序会随着时间的推移保持不变。”
  • 具体来说,地图接口不传达任何排序。有一些 Map 的实现是有序的,例如 TreeMap 和 LinkedHashMap。你可以看看那些。 (但应该注意,这些仅授予“可预测的迭代顺序” - 您不能只要求第 425 个条目而不迭代第一个 424。不理想!)
  • @AndyTurner 我同意,即使是一个已排序映射的TreeMap,也不一定公开对索引的访问。

标签: java performance hashmap iterator


【解决方案1】:

HashMap 不保留插入顺序。

如果您总是想根据索引值或其插入顺序检索数据,那么我建议您使用List 实现,例如保证插入顺序的ArrayList .

您可以在主数据对象周围创建一个包装器对象并将它们放在ArrayList 中,当您需要读取它时,您可以使用get 方法使用您要读取的索引值。

【讨论】:

    【解决方案2】:

    HashMap 不打算以这种方式使用,因为不能保证条目的顺序。如果您确实需要 key->value 结构,最好使用ArrayListLinkedHashMap

    【讨论】:

      【解决方案3】:

      没有有效的方法可以从HashMap 中提取第 i 个条目。实际上,HashMap 中的第 i 个条目甚至都不是一个定义明确的概念,因为 HashMap 中条目的顺序是未指定的。

      (相比之下,LinkedHashMap 的条目可以按照插入条目的顺序进行迭代。然而,即使对于 LinkedHashMap,除了从头开始迭代之外,也无法“索引”这些条目,这是一个O(I) 操作,其中I 是您尝试检索的元素的索引。)

      底线:如果您想使用索引进行高效(即O(1))查找,您应该使用ArrayList 或原始数组。

      (或者,也许,使用索引值作为哈希表的键,或者主哈希表中条目的单独哈希表。但是你说的是更复杂的数据结构和/或不同的“索引”模型。)

      【讨论】:

      • 太棒了,谢谢斯蒂芬!我希望有一个不同的答案,但没关系 - 知道这些限制很酷。 :)
      【解决方案4】:

      如果您不知道密钥,那么 HashMap 就毫无用处! 改用 ArrayList 或类似的。

      如果您的 HashMap 真的非常非常大(即它不适合您的可用内存),那么您可以考虑使用以下类似的东西: http://www.oracle.com/technetwork/database/berkeleydb/overview/index-093405.html

      【讨论】:

      • 对不起,我真的想说“谢谢!”刚刚重读了之前的评论,害怕听起来很讽刺……如果是这样,讽刺完全是无意的。
      【解决方案5】:

      顾名思义,“散列映射”暗示,底层数据结构是“散列表”。从概念上讲,它是一系列“桶”,并且密钥被“散列”以确定要查看哪个(一个)桶以尝试找到该密钥。这是一种非常有效的按值查找键的数据结构,但它没有“顺序”的概念。

      Java 有非常丰富的数据结构替代方案:各种树、集合等。甚至是很好的数组!您需要选择更适合您需要的不同结构。

      (请记住,某些东西可以“在”……也就是说,“被引用”……一次不止一个这样的容器,其方式与 SQL 表可能具有的方式大致相同多个索引。)

      【讨论】:

      • 非常有帮助,谢谢!当我编写此代码的下一个版本时,我会考虑这些存储桶。 :)
      猜你喜欢
      • 1970-01-01
      • 2019-03-09
      • 1970-01-01
      • 2011-06-09
      • 2014-12-03
      • 2012-04-28
      • 2021-11-10
      • 2016-03-08
      • 2020-06-23
      相关资源
      最近更新 更多