【问题标题】:Java optimization, gain from hashMap?Java优化,从hashMap中获益?
【发布时间】:2009-10-31 02:11:49
【问题描述】:

我得到了一些 可爱的 Java 代码,其中包含很多类似的东西(在一个执行大约 150 万次的循环中)。

code = getCode();
for (int intCount = 1; intCount < vA.size() + 1; intCount++)
{
   oA = (A)vA.elementAt(intCount - 1);
   if (oA.code.trim().equals(code))
       currentName= oA.name;
}

我会看到从切换到类似以下内容的速度显着提高

code = getCode();
//AMap is a HashMap
strCurrentAAbbreviation = (String)AMap.get(code);

编辑: vA 的大小约为 50。修剪不应该甚至是必要的,但调用 50 次而不是 50* 肯定会很好150万。 vA 中的项目是唯一的。

编辑:在几位响应者的建议下,我对其进行了测试。结果在底部。谢谢大家。

【问题讨论】:

  • 你也可以通过使用泛型来摆脱丑陋的演员阵容:声明你的 HashMap 是从字符串到 A。
  • 我喜欢循环中使用基于 1 的循环索引的部分,通过测试 intCount
  • vA 中的元素是否应该是唯一的?
  • 这是在 Java 1.4 中,所以很遗憾没有泛型。一旦我们升级,我就会转换(我希望很快!)。

标签: java optimization hashmap


【解决方案1】:

只有一种方法可以找出答案。

【讨论】:

  • 我做了,在底部看到我的答案。
【解决方案2】:

好的,好的,我测试过了。

结果如下,供您启迪:

循环:18391 毫秒 哈希:218ms

循环:18735ms 哈希:234ms

循环:18359 毫秒 哈希:219ms

我想我会重构那一点..

框架:

public class OptimizationTest {
    private static Random r = new Random();
    public static void main(String[] args){
        final long loopCount = 1000000;
        final int listSize = 55;

        long loopTime = TestByLoop(loopCount, listSize);
        long hashTime = TestByHash(loopCount, listSize);
        System.out.println("Looping: " + loopTime + "ms");
        System.out.println("Hash: " + hashTime + "ms");
    }
    
    public static long TestByLoop(long loopCount, int listSize){
        Vector vA = buildVector(listSize);
        A oA;

        StopWatch sw = new StopWatch();
        sw.start();
        for (long i = 0; i< loopCount; i++){
            String strCurrentStateAbbreviation;
            int j = r.nextInt(listSize);
            for (int intCount = 1; intCount < vA.size() + 1; intCount++){
                oA = (A)vA.elementAt(intCount - 1);
                if (oA.code.trim().equals(String.valueOf(j)))
                    strCurrentStateAbbreviation = oA.value;
            }
        }
        sw.stop();
        return sw.getElapsedTime();
    }
    
    public static long TestByHash(long loopCount, int listSize){
        HashMap hm = getMap(listSize);
        StopWatch sw = new StopWatch();
        sw.start();
        String strCurrentStateAbbreviation;
        for (long i = 0; i < loopCount; i++){
            int j = r.nextInt(listSize);
            strCurrentStateAbbreviation = (String)hm.get(j);
        }
        sw.stop();
        return sw.getElapsedTime();
    }
    
    private static HashMap getMap(int listSize) {
        HashMap hm = new HashMap();
        for (int i = 0; i < listSize; i++){
            String code = String.valueOf(i);
            String value = getRandomString(2);
            hm.put(code, value);
        }
        return hm;
    }

    public static Vector buildVector(long listSize) 
    {
        Vector v = new Vector();
        for (int i = 0; i < listSize; i++){
            A a = new A();
            a.code = String.valueOf(i);
            a.value = getRandomString(2);
            v.add(a);
        }
        return v;
    }
    
    public static String getRandomString(int length){
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i< length; i++){
            sb.append(getChar());
        }
        return sb.toString();
    }
    
    public static char getChar()
    {
        final String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        int i = r.nextInt(alphabet.length());
        return alphabet.charAt(i);
    }
}

【讨论】:

  • +1 用于测试,并以合法答案回答您自己的问题。可惜不能+2。
  • 请将这些内容添加到原始问题中。把东西放在一起。
  • +1。这么多关于 SO 的问题都可以用同样的方式回答;很少有人愿意接受建议!
【解决方案3】:

嗯,你很有可能会,是的。如果您有良好的哈希码,从 HashMap 中检索将是恒定的时间。

但你能真正找到答案的唯一方法就是尝试一下。

【讨论】:

    【解决方案4】:

    这取决于你的地图有多大,以及你的 hashCode 实现有多好(这样你就不会有 colisions)。

    【讨论】:

      【解决方案5】:

      你真的应该做一些真正的分析来确定是否需要任何修改,因为你最终可能会花时间修复一些没有损坏的东西。

      在我看来,比 elementAt 调用更突出的是您在每次迭代中所做的字符串修整。我的直觉告诉我,这可能是一个更大的瓶颈,但只有分析才能真正说明问题。

      祝你好运

      【讨论】:

      • 是的,当您调用它十亿次时,trim() 可能也很昂贵。在这里使用 Map 的好处是您可以一次 trim() 键。
      【解决方案6】:

      我会说是的,因为上面似乎是对 vA.size() 的线性搜索。 va 有多大?

      【讨论】:

        【解决方案7】:

        为什么不使用 YourKit 之类的东西(或插入另一个分析器)来看看这部分循环的成本有多大。

        【讨论】:

          【解决方案8】:

          使用地图肯定是一种改进,有助于以后维护该代码。

          是否可以使用地图取决于(矢量?)是否包含唯一代码。给定的 for 循环将记住列表中具有给定代码的 last 对象,这意味着哈希不是解决方案。

          对于较小(稳定)的列表大小,只需将列表转换为对象数组即可显示性能提升以及一些更好的可读性。

          如果以上都不成立,至少使用迭代器检查列表,提供更好的可读性和一些(可能的)性能提升。

          【讨论】:

            【解决方案9】:

            视情况而定。你有多少内存?

            我会猜得更快,但要分析一下。

            【讨论】:

              【解决方案10】:

              我认为这里的主要因素是 vA 有多大,因为循环需要运行 n 次,其中 n 是 vA 的大小。有了地图,无论 vA 有多大,都没有循环。因此,如果 n 很小,则改进将很小。如果它是巨大的,那么改进将是巨大的。尤其如此,因为即使在找到匹配元素之后,循环也会继续进行!因此,如果您在 200 万个元素列表的第 1 个元素处找到匹配项,您仍然需要检查最后 1,999,999 个元素!

              【讨论】:

                【解决方案11】:

                是的,它几乎肯定会更快。假设您的 vA 内容可散列,平均循环 25 次(50 次的中途)比 hashmap 查找要慢。

                但是,说到您的 vA 内容,您必须在将它们插入 aMap 时对其进行修剪,因为 aMap.get("somekey") 不会找到键为“某个键”。

                实际上,您应该在插入 vA 时这样做,即使您不切换到 hashmap 解决方案。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2010-12-17
                  • 2014-06-30
                  • 1970-01-01
                  • 2020-11-25
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-11-25
                  相关资源
                  最近更新 更多