【发布时间】:2015-01-04 17:37:27
【问题描述】:
我曾认为 HashMaps 对单个值的随机访问比 ArrayLists 更快。 . .也就是说,HashMap.get(key) 应该比ArrayList.get(index) 快,因为ArrayList 必须遍历集合的每个元素才能达到其值,而HashMap 则不然。你知道,O(1) vs O(n) 等等。
编辑: 所以我对HashMaps 的理解不够充分,因此我很困惑。此代码的结果符合预期。感谢您的许多解释。
所以我决定试一试。这是我的代码:
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()与3999的短键关联的值所用的时间。
经过 2-3 个预热周期后,测试表明 ArrayList.get() 大约需要 56 ns,而 HashMap.get() 大约需要 68 ns。那是 。 . .不是我所期望的。我的HashMap 都被碰撞吃光了吗?所有的关键条目都应该自动装箱到 Shorts,它们应该报告它们存储的短值以响应.hashcode(),所以所有的哈希码都应该是唯一的。我想?
即使没有预热,ArrayList.get() 仍然更快。这与我在其他地方看到的一切相反,例如this question。当然,我还读到使用ListIterator 遍历ArrayList 比仅在循环中使用.get() 更快,显然情况并非如此。 . .
【问题讨论】:
-
数组列表为什么会遍历列表?该元素位于给定索引处的数组中。在编写所有这些之前,您可以阅读 ArrayList 的代码来检查您的假设。
-
是的,我知道这个名字会让人感到困惑。它是一个实现为数组数据结构的列表。也许您正在考虑一个链表数据结构,这是不同的。我记得我第一次看到它时也有点被甩了。
-
另外,对于微观基准,使用 JMH 或 caliper。
-
尝试不同的
hashCode()实现并比较结果。 -
@xdhmoore 我认为这是其中的一部分,尽管实际上我在这里表现出很多无知,无法理解 ArrayList 如何或何时提供 O(n) 性能以及 HashMap 在哪里将在相同的情况下提供 O(1) 性能。这是一个经常被引用的事实,我认为这是一个涉及许多不相关操作的全面声明是理所当然的。这是我的谬误。
标签: java performance arraylist hashmap