remove(int index) Самостоятельная реализация LinkedList

Я пытаюсь изучить производительность LinkedList по сравнению с ArrayList. Я сделал свой метод удаления следующим образом.

Данные В LinkedList, который удаляется, находится около 1 миллиона элементов. Моя проблема после удаления всех элементов: пришло время записи. Если я использую Java LinkedList remove(int index) Время: 2000 наносекунд Если я использую свой Custom remove(int index) Время: 34407000 наносекунд

Может кто-нибудь посмотреть мой код и сказать мне, где я ошибаюсь. На самом деле я предполагаю удалить данные по позициям индекса, поскольку сравнение, которое я пытаюсь достичь, выполняется по позициям индекса для ArrayList.

public Object remove(int index)
{
    checkElementIndex(index);
    return unlink(getNode(index));
}

private Object unlink(ListNode node)
{
    final Object element = node.item;
    final ListNode next = node.next;
    final ListNode prev = node.prev;

    if (prev == null)
    {
        first = next;
    } else
    {
        prev.next = next;
        node.prev = null;
    }

    if (next == null)
    {
        last = prev;
    } else
    {
        next.prev = prev;
        node.next = null;
    }

    node.item = null;
    size--;
    return element;
}

private ListNode getNode(int index)
{
    if (index < (size >> 1))
    {
        ListNode node = first;
        for (int i = 0; i < index; i++)
        {
            node = node.next;
        }
        return node;
    } else
    {
        ListNode node = last;
        for (int i = size - 1; i > index; i--)
        {
            node = node.prev;
        }
        return node;
    }
}

private void checkElementIndex(int index)
{
    if (index < 0 || index >= size)
    {
        throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
    }
}

    // BOTH THE LIST CONTAIN 1million items.

    startTime = System.nanoTime();
    for (int i = linkedList.size()-1; i >= 0; i--)
    {
        linkedList.remove(i);
    }
    endTime = System.nanoTime();
    duration = endTime - startTime;
    System.out.println("LinkedList Removal Time:  " + duration);

    // This is the Java Collection LinkedList
    startTime = System.nanoTime();
    for (int i = linkedList.size()-1; i >= 0; i--)
    {
        javaLinkedList.remove(i);

    }
    endTime = System.nanoTime();
    duration = endTime - startTime;
    System.out.println("My Removal Time:  " + duration);

Я ценю каждое возможное предложение. Спасибо.


person user3337714    schedule 07.05.2014    source источник
comment
Хороший способ выяснить это самостоятельно — использовать профилировщик, такой как VisualVM.   -  person awksp    schedule 07.05.2014
comment
Вы можете опубликовать реализацию checkElementIndex?   -  person Kakarot    schedule 07.05.2014
comment
Хммм... Этот код в значительной степени является точной копией реализации Java LinkedList, а это означает, что, скорее всего, проблема не в этих методах.   -  person awksp    schedule 07.05.2014
comment
@Kakarot Я добавил checkElementIndex в приведенный выше код.   -  person user3337714    schedule 07.05.2014
comment
@user3580294 user3580294 Довольно много. Но у меня есть какие-то другие методы, которые не связаны с этими методами, но все же большая разница во времени, которую я не могу понять.   -  person user3337714    schedule 07.05.2014
comment
Ваш код достаточно близок к Java-версии LinkedList, поэтому я могу предположить, что эти методы не являются проблемой, если только я что-то не пропустил. Попробуйте профилировщик. Кроме того, как вы синхронизируете свой код?   -  person awksp    schedule 07.05.2014
comment
@ user3580294 System.nanoTime() как в myLinkedList, так и в коллекции Java. Я не знаю, как использовать профайлер. (NewBie :( Извините). Каждый метод начинается и заканчивается с помощью startTime и endTime, а разница заключается в продолжительности.   -  person user3337714    schedule 07.05.2014
comment
Что произойдет, если вы поменяете местами связанный список Java и ваш связанный список?   -  person awksp    schedule 07.05.2014
comment
Кроме того, вы знаете, как использовать профайлер?   -  person awksp    schedule 07.05.2014
comment
@user3580294 user3580294 Реверс: Как бы я это сделал. Java Collection LinkedList - это конкретный класс, я не смог бы запустить на нем свой метод, если бы я каким-то образом не расширил их? (Я могу ошибаться?)   -  person user3337714    schedule 07.05.2014
comment
Я имею в виду запуск связанного списка Java в первом цикле и вашу реализацию во втором.   -  person awksp    schedule 07.05.2014
comment
@user3580294 user3580294 Я только что сделал это. Время удаления JavaLinkedList: 37232000 наносекунд (это увеличилось?) Время удаления моего LinkedList: 36427000 наносекунд   -  person user3337714    schedule 07.05.2014
comment
Ах, вот и мы. Во-первых, прочитайте как провести микротестирование в Java . Опять же, я настоятельно рекомендую профилировать код с помощью чего-то вроде VisualVM. Честно говоря, этот код выглядит нормально, и я не могу понять, что происходит.   -  person awksp    schedule 07.05.2014
comment
Мне жаль, что я не мог заметить ничего очевидного, но мне любопытно, что происходит сейчас...   -  person awksp    schedule 07.05.2014
comment
@user3580294 user3580294 Значит, мне стоит попробовать разогреться? Ну, я не знаю об этом и как это работает. Я читаю статью, которую вы упомянули в комментарии, и это имеет смысл. Я бы попытался понять и посмотреть, смогу ли я тоже что-нибудь сделать. Это сильно влияет на производительность сравнения ArrayList и LinkedList.   -  person user3337714    schedule 07.05.2014
comment
Опять же, первое, что я бы сделал, это фактически профилировал ваш код. Микробенчмарки довольно сложно провести правильно, и, на мой взгляд, профилирование — гораздо более эффективный способ увидеть, где находятся ваши узкие места, тем более что профайлер также скажет вам, в каком методе вы тратите больше всего времени. Если вы хотите попробовать профилировать, я будем рады провести вас через этот процесс. Если вы настроены на микробенчмарки, я бы очень внимательно последовал совету в этой статье, но, как я уверен, вы читали, есть много деталей, которые вам нужно сделать правильно, если вы хотите получить полезные результаты.   -  person awksp    schedule 07.05.2014
comment
@user3580294 user3580294 Я думаю, что профилирование было бы хорошей идеей. Не могли бы вы помочь мне с этим заявлением? (Это очень любезно с вашей стороны помочь мне, спасибо).   -  person user3337714    schedule 07.05.2014
comment
Я перехожу в раздел ответов для большего количества места.   -  person awksp    schedule 07.05.2014


Ответы (1)


(продолжение из комментариев)

Без проблем! Первый шаг — загрузить профилировщик (я использую VisualVM. Есть и другие, но, к сожалению, я не знаком с ними ...) Как только это будет загружено, запустите его.

Следующий шаг — выяснить, как подключить профилировщик к вашему процессу. После запуска VisualVM вы должны увидеть список запущенных Java-программ справа. Вы можете пока игнорировать это. Хитрость в том, что вам понадобится долго работающая программа, чтобы иметь достаточно времени, чтобы подключить профилировщик к процессу. Для чего-то вроде вашего кода проще было бы использовать Scanner.nextLine() для блокировки программы между циклами, примерно так:

Scanner scanner = new Scanner(System.in);
scanner.nextLine(); // This stops the program and waits for user input.
                    // This will give us all the time in the world to attach the profiler.

// Your code
for (int i = linkedList.size()-1; i >= 0; i--)
{
    linkedList.remove(i);
}

scanner.nextLine(); // Same thing here
for (int i = linkedList.size()-1; i >= 0; i--)
{
    javaLinkedList.remove(i);
}

Теперь приступайте к своей программе. Если вы вернетесь в VisualVM, вы сможете увидеть свою программу!

Теперь дважды щелкните вашу программу. Вы должны увидеть несколько сообщений, появившихся в консоли вашей программы, и представление в VisualVM должно измениться. Перейдите на вкладку «Sampler», нажмите «CPU», затем вернитесь к своей программе и нажмите Enter.

То, что вы видите, должно быть довольно понятным. Слева приведены полные имена методов с пакетом + классом, а также полоса, представляющая долю процессорного времени, используемого методами. Используйте это, чтобы определить, где ваша программа тратит все свое время. И вот! Если вам нужно больше времени для профилирования, просто добавьте больше элементов в связанные списки.

Просто предостережение; профилирование может быть обманчивым, потому что, если другой метод испортит структуру вашего связанного списка, он может заставить работать совершенно хороший метод гораздо тяжелее, чем должен. Например, мне пришлось реализовать HashMap как часть школьного задания. Когда я профилировал, я заметил, что код для поиска в сегментах занимает 97%+ процессорного времени, хотя в этом нет ничего плохого. Оказывается, в тесте на правильное ведро было >> вместо <<, вместо этого HashMap превратилось в LinkedList! Поэтому, хотя профилирование — хорошее начало (и обычно это единственное, что вам нужно сделать для выявления проблем), просто имейте в виду, что ошибки могут быть где угодно.

Я надеюсь, что это помогло!

person awksp    schedule 07.05.2014
comment
Я просто читаю, и это действительно имеет большой смысл. Я просто загружаю VisualVM и собираюсь прикрепить то же, что и вы. Я уверен, что это будет намного лучше. Это обязательно даст мне некоторые подробности о моих методах и моих будущих проектах. Было странно, что код работает быстрее после прогрева или инициализации того же кода (через Java или MyCode для LinkedList). - person user3337714; 07.05.2014
comment
На самом деле это вовсе не так уж и странно. В Java есть так называемый своевременный компилятор (JIT-компилятор), который отслеживает код, который вы запускаете. Если он обнаруживает некоторый горячий код (код, который занимает много процессорного времени или часто запускается), он перекомпилирует байт-код Java до собственного кода. Это почти всегда приводит к ускорению по сравнению с байт-кодом Java. Причина периода прогрева заключалась в том, чтобы заставить JVM перекомпилировать весь код, который вы хотели протестировать, удалив из эталонного теста фактор, когда JIT-собирается-активироваться. - person awksp; 07.05.2014
comment
Я думаю, что мне следует помнить об этом, когда я буду делать будущие проекты и пытаться использовать удобную функцию. Я все равно посмотрю, ускорится ли мой код :-/. - person user3337714; 07.05.2014
comment
Профилировщики бесценны, если вас интересует производительность. Есть несколько других полезных функций VisualVM, таких как использование кучи/памяти, если вы беспокоитесь о чрезмерном создании объектов. - person awksp; 07.05.2014
comment
Я учусь этим вещам, и они определенно будут бесценны для меня, и я буду благодарен вам каждый раз, когда буду их использовать. :) - person user3337714; 07.05.2014