Java итерированный HashTable против скорости ArrayList - PullRequest
5 голосов
/ 15 декабря 2011

Я пишу простой движок 3D SW рендеринга. У меня есть значение по умолчанию ArrayList<Object3D>, содержащее всю сцену. Теперь я хочу иметь возможность добавлять, удалять и выбирать объекты по именам, как это делают 3D-редакторы (потому что это НАМНОГО проще, чем выделение мышью, но при этом хорошо выглядит в домашней работе :)).

Итак, первое, что я подумал, это иметь Hashtable для имени и индекс для сцены ArrayList. Но потом я подумал, что могу просто сохранить сцену, используя Hashtable напрямую, и пройти через нее для рендеринга с использованием итератора.

Итак, я хочу спросить, в 3D-движке, что предпочтительнее по скорости? Потому что я буду зацикливать сцену много раз в секунду, по сравнению с выбором объекта. ArrayList быстрее iterated Hashtable? Спасибо.

Ответы [ 6 ]

6 голосов
/ 15 декабря 2011

Во-первых, я предлагаю вам использовать 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 ГГц и большим количеством свободной памяти.

1 голос
/ 15 декабря 2011

Я вполне уверен, что итерация по ArrayList будет быстрее, чем итерация по Hashtable.Не уверен, насколько значительна разница - возможно (большой палец сосет) в 2 раза в реальной внутренней логике, но это не так много.

Но учтите, что, если вам не нужна многопоточная синхронизация, вы должны использовать HashMap а не Hashtable.Там есть какая-то производительность.

0 голосов
/ 15 декабря 2011

Как уже говорилось, лучше использовать HashMap.Что касается итерации, теоретически, ArrayList должен быть быстрее по двум причинам.Во-первых, структура данных проще, что дает меньше времени доступа.Во-вторых, ArrayList можно итерировать по индексу, не создавая объект Iterator, который в случае интенсивного использования производит меньше мусора и, следовательно, меньше gc.На практике - вы можете не заметить разницы, зависит от того, насколько тяжелым вы собираетесь его использовать.

0 голосов
/ 15 декабря 2011

На самом деле, я посмотрел на текущую реализацию HashMap (предпочтительнее, чем Hashtable, как все указывают).Итерирование по значениям выглядит так, как будто это просто итерация по базовому массиву.

Таким образом, скорость, вероятно, будет сравнима с итерацией ArrayList, хотя может быть некоторое время, пропуская пропуски в базовом HashMap sмассив.

Все сказано, профилирование - король.

0 голосов
/ 15 декабря 2011

Используйте java.util.HashMap вместо java.util.Hashtable, если вам не нужна поисковая синхронизация.

0 голосов
/ 15 декабря 2011

A) не используйте Hashtable, используйте HashMap. Hashtable неофициально устарела

B) Это зависит от приложения. Поиск будет быстрее в HashMap, итерация, вероятно, будет такой же, как оба используют массивы внутри. (но массивы в HashMap имеют пробелы, так что это может дать небольшое преимущество ArrayList). Да, и если вы хотите сохранить фиксированный порядок итераций, используйте LinkedHashMap (отсортировано по вставке) или TreeMap (отсортировано по естественному порядку)

...