ArrayList .get比HashMap .get更快?

时间:2014-11-07 20:26:56

标签: java performance arraylist hashmap

我原以为HashMap s对于单个值的随机访问比ArrayList更快。 。 。也就是说,HashMap.get(key)应该比ArrayList.get(index)更快,因为ArrayList必须遍历集合的每个元素才能达到其值,而HashMap则不。您知道,O(1) vs O(n)以及所有这些。

编辑:所以我对HashMap的理解是不充分的,因此我的困惑。此代码的结果符合预期。感谢您的解释。

所以我决定用百灵鸟来测试它。这是我的代码:

import java.util.HashMap;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Scanner;

public class Testing
{       

    public static void main(String[] args)
    {
        ArrayList<SomeClass> alist = new ArrayList<>();
        HashMap<Short, SomeClass> hmap = new HashMap<>(4000, (float).75);
        ListIterator<SomeClass> alistiterator = alist.listIterator();
        short j = 0;
        do
        {
            alistiterator.add(new SomeClass());
            j++;
        }
        while(j < 4000);
        for (short i = 0; i < 4000; i++)
        {
             hmap.put(i, new SomeClass());
        }
        boolean done = false;
        Scanner input = new Scanner(System.in);
        String blargh = null;
        do
        {
            System.out.println("\nEnter 1 to run iteration tests.");
            System.out.println("Enter w to run warmup (recommended)");
            System.out.println("Enter x to terminate program.");
            try
            {
                blargh = input.nextLine();
            }
            catch (NoSuchElementException e)
            {
                System.out.println("Uh, what? Try again./n");
                continue;
            }
            switch (blargh)
            {
                case "1":
                    long starttime = 0;
                    long total = 0;
                    for (short i = 0; i < 1000; i++)
                    {
                        starttime = System.nanoTime();
                        iteratearraylist(alist);
                        total += System.nanoTime() - starttime;
                    }
                    total = (long)(total * .001);
                    System.out.println(total + " ns: iterating sequentially"
                                       + " through ArrayList");
                    total = 0;
                    for (short i = 0; i< 1000; i++)
                    {
                        starttime = System.nanoTime();
                        iteratearraylistbyget(alist);
                        total += System.nanoTime() - starttime;
                    }
                    total = (long)(total * .001);                   
                    System.out.println(total + " ns: iterating sequentially"
                                       + " through ArrayList via .get()");
                    total = 0;
                    for (short i = 0; i< 1000; i++)
                    {
                        starttime = System.nanoTime();
                        iteratehashmap(hmap);
                        total += System.nanoTime() - starttime;
                    }
                    total = (long)(total * .001);           
                    System.out.println(total + " ns: iterating sequentially"
                                       + " through HashMap via .next()");
                    total = 0;
                    for (short i = 0; i< 1000; i++)
                    {
                        starttime = System.nanoTime();
                        iteratehashmapbykey(hmap);
                        total += System.nanoTime() - starttime;
                    }                   
                    total = (long)(total * .001);       
                    System.out.println(total + " ns: iterating sequentially"
                                       + " through HashMap via .get()");
                    total = 0;
                    for (short i = 0; i< 1000; i++)
                    {
                        starttime = System.nanoTime();
                        getvaluebyindex(alist);
                        total += System.nanoTime() - starttime;
                    }               
                    total = (long)(total * .001);       
                    System.out.println(total + " ns: getting end value"
                                   + " from ArrayList");
                    total = 0;
                    for (short i = 0; i< 1000; i++)
                    {
                        starttime = System.nanoTime();
                        getvaluebykey(hmap);
                        total += System.nanoTime() - starttime;
                    }           
                    total = (long)(total * .001);       
                    System.out.println(total + " ns: getting end value"
                               + " from HashMap");
                    break;
                case "w":
                    for (int i = 0; i < 60000; i++)
                    {
                        iteratearraylist(alist);
                        iteratearraylistbyget(alist);
                        iteratehashmap(hmap);
                        iteratehashmapbykey(hmap);
                        getvaluebyindex(alist);
                        getvaluebykey(hmap);
                    }
                    break;
                case "x":
                    done = true;
                    break;
                default:
                    System.out.println("Invalid entry.  Please try again.");
                    break;
            }           
        }
        while (!done);
        input.close();
    }

    public static void iteratearraylist(ArrayList<SomeClass> alist)
    {
        ListIterator<SomeClass> tempiterator = alist.listIterator();
        do
        {
            tempiterator.next();
        }
        while (tempiterator.hasNext());
    }

    public static void iteratearraylistbyget(ArrayList<SomeClass> alist)
    {
        short i = 0;        
        do
        {
            alist.get(i);
            i++;
        }
        while (i < 4000);
    }

    public static void iteratehashmap(HashMap<Short, SomeClass> hmap)
    {
        Iterator<HashMap.Entry<Short, SomeClass>> hmapiterator = 
        map.entrySet().iterator();
        do
        {
            hmapiterator.next();
        }
        while (hmapiterator.hasNext());
    }   

    public static void iteratehashmapbykey(HashMap<Short, SomeClass> hmap)
    {
        short i = 0;
        do
        {
            hmap.get(i);
            i++;
        }
        while (i < 4000);
    }

    public static void getvaluebykey(HashMap<Short, SomeClass> hmap)
    {
        hmap.get(3999);
    }

    public static void getvaluebyindex(ArrayList<SomeClass> alist)
    {
        alist.get(3999);
    }
}

public class SomeClass
{
    int a = 0;
    float b = 0;
    short c = 0;

    public SomeClass()
    {
        a = (int)(Math.random() * 100000) + 1;
        b = (float)(Math.random() * 100000) + 1.0f;
        c = (short)((Math.random() * 32000) + 1);
    }
}

有趣的是,代码似乎分阶段热身。我已经确定的最后阶段是在所有方法的大约120,000次迭代之后。无论如何,在我的测试机器上(AMD x2-220,L3 + 1额外核心解锁,3.6 ghz,2.1 ghz NB),真正跳出来的数字是最后两个报道。即,.get()ArrayList)的最后一个条目的index == 3999所花费的时间以及.get()与{{1}的短键相关联的值所花费的时间}。

在2-3个预热周期后,测试显示3999大约需要56 ns,而ArrayList.get()大约需要68 ns。那是 。 。 。不是我的预期。我的HashMap.get()是否都因碰撞而吃掉了?所有密钥条目都应该自动提交给Shorts,它们应该报告它们存储的短值以响应HashMap,因此所有的哈希码都应该是唯一的。我想?

即使没有热身,.hashcode()仍然更快。这与我在其他地方看到的一切相反,例如this question。当然,我还读到,使用ArrayList.get()遍历ArrayList比在循环中使用ListIterator更快,显然,情况并非如此。 。

5 个答案:

答案 0 :(得分:5)

ArrayList.get(index)实际上使用常量时间,因为ArrayList由数组支持,所以它只是在烘焙数组中使用该索引。在最坏的情况下,ArrayList.contains(Object)O(n)的长期操作。

答案 1 :(得分:5)

Hashmaps在检索已知索引处的某些内容时速度不快。如果您按照已知的顺序存储内容,则列表将获胜。

但是,不是将所有内容插入到列表1-4000中的示例,而是以完全随机顺序执行此操作。现在要从列表中检索正确的项目,您必须逐个检查每个项目以查找正确的项目。但是要从hashmap中检索它,你需要知道的只是插入它时给它的关键。

所以,你应该将Hashmap.get(i)与

进行比较
for(Integer i : integerList)
   if(i==value)
       //found it!

然后你会看到hashmap的真正效率。

答案 2 :(得分:4)

  

ArrayList必须遍历集合的每个元素才能达到其值

事实并非如此。 ArrayList由一个数组支持,该数组允许进行恒定时间get操作。

另一方面,

HashMap&#39; s get首先必须对其参数进行哈希处理,然后它必须遍历哈希码对应的桶,测试桶中的每个元素与给定密钥相等。这通常比仅索引数组要慢。

答案 3 :(得分:3)

HashMap的大O是O(1+α)。你的α来自哈希码冲突,必须遍历一个桶来检查是否相等。

enter image description here

用于通过索引ArrayList

O(1)中提取项目的大O.

enter image description here

如果有疑问......把它画出来......

答案 4 :(得分:1)

ArrayListHashMap都支持数组,HashMap必须计算密钥的哈希码,从中派生用于访问数组的索引,同时访问数组和您使用get的ArrayList中的元素提供索引。所以它的3个操作与ArrayList的1个操作相比。

但是,ListMap是否支持数组是实现细节。所以答案可能因您使用的实现而有所不同。