Во-первых, я предлагаю вам использовать HashMap
вместо Hashtable
, по той же причине, по которой ArrayList
является лучшим выбором, чем Vector
: меньше накладных расходов из-за бесполезной синхронизации.
Я предполагаю, что итерация по ArrayList
будет быстрее, чем итерация по Set
, возвращаемому методом Hashtable
(или HashMap
) entrySet()
. Но единственный способ узнать это профиль.
Очевидно, что изменения в списке отображения (кроме добавления или удаления последнего элемента) будут быстрее для HashMap
, чем для ArrayList
.
EDIT
Таким образом, я следовал своему собственному совету и оценил. Вот код, который я использовал:
import java.util.*;
public class IterTest {
static class Thing {
Thing(String name) { this.name = name; }
String name;
}
static class ArrayIterTest implements Runnable {
private final ArrayList<Thing> list;
ArrayIterTest(ArrayList<Thing> list) {
this.list = list;
}
public void run() {
int i = 0;
for (Thing thing : list) {
++i;
}
}
}
static class ArraySubscriptTest implements Runnable {
private final ArrayList<Thing> list;
ArraySubscriptTest(ArrayList<Thing> list) {
this.list = list;
}
public void run() {
int i = 0;
int n = list.size();
for (int j = 0; j < n; ++j) {
Thing thing = list.get(j);
++i;
}
}
}
static class MapIterTest implements Runnable {
private final Map<String, Thing> map;
MapIterTest(Map<String, Thing> map) {
this.map = map;
}
public void run() {
int i = 0;
Set<Map.Entry<String, Thing>> set = map.entrySet();
for (Map.Entry<String, Thing> entry : set) {
++i;
}
}
}
public static void main(String[] args) {
final int ITERS = 10000;
final Thing[] things = new Thing[1000];
for (int i = 0; i < things.length; ++i) {
things[i] = new Thing("thing " + i);
}
final ArrayList<Thing> arrayList = new ArrayList<Thing>();
Collections.addAll(arrayList, things);
final HashMap<String, Thing> hashMap = new HashMap<String, Thing>();
for (Thing thing : things) {
hashMap.put(thing.name, thing);
}
final ArrayIterTest t1 = new ArrayIterTest(arrayList);
final ArraySubscriptTest t2 = new ArraySubscriptTest(arrayList);
final MapIterTest t3 = new MapIterTest(hashMap);
System.out.println("t1 time: " + time(t1, ITERS));
System.out.println("t2 time: " + time(t2, ITERS));
System.out.println("t3 time: " + time(t3, ITERS));
}
private static long time(Runnable runnable, int iters) {
System.gc();
long start = System.nanoTime();
while (iters-- > 0) {
runnable.run();
}
return System.nanoTime() - start;
}
}
А вот результаты типичного прогона:
t1 time: 41412897
t2 time: 30580187
t3 time: 146536728
Очевидно, что использование ArrayList - это большой выигрыш (в 3-4 раза) над HashMap, по крайней мере, для моего стиля итерации через HashMap. Я подозреваю, что причина того, что итератор массива медленнее, чем подписка массива, заключается во всех объектах итератора, которые необходимо создать, а затем собрать мусор.
Для справки, это было сделано с помощью Java 1.6.0_26 (64-битная JVM) на четырехъядерной машине Intel с частотой 1,6 ГГц и большим количеством свободной памяти.