Интрузивная реализация списка для Java?

Есть ли какой-либо (хорошо реализованный) intrusive двойной связанный список классов (ов), доступных для Java? Или я должен делать свое? У Boost есть это для С++: http://beta.boost.org/doc/libs/1_40_0/doc/html/boost/intrusive/list.html.

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

Я пытаюсь привести соответствующий пример. Предположим, что у меня есть связанные списки L list1 и L list2, принадлежащие разным типам классов X1 и Y2.

класс Q (которому нечего делать или не легко получить доступ к интерфейсам x1 и Y2 в противном случае) необходимо выполнить i) заменить, ii) удалить операцию для элемента e, которая всегда существует где-то в списке1 xor list2 в зависимости от состояния времени выполнения, но эта информация не сохраняется в любом месте.

С интрузивным списком Q может просто хранить ссылку на элемент на элемент элемента e, и он всегда указывает правильное место.

В противном случае вам придется выбирать из нескольких более сложных обходных решений одного или другого. - класс обертки для элемента e и дополнительные методы для завершения операций я и ii. Нет.

В принципе, вопрос по-прежнему не связан с производительностью, а с архитектурной сложностью. Это также можно понимать как один вид ситуации с общими объектами, где решение IL избегает необходимости обновления для каждого клиента Lx и Q.

Обратите внимание, что мне НЕ нужна совместимость с другими стандартными контейнерами. Просто общая интрузивная реализация lsit с повторением, добавлением, удалением и обнаружением операций, используемых с неизвестным классом элемента.

Спасибо.

Ответ 1

Я не знаю никаких существующих реализаций (и нет, я не считаю обычные коллекции Java интрузивными).

Вероятно, потому, что единственным основным преимуществом такого списка в Java было бы быстрое вызов remove(), когда у вас уже есть элемент, который нужно удалить под рукой (и не имеет Iterator в этой позиции), Не-копирование-элемент не является допустимым аргументом в Java, поскольку реализации Java List обрабатывают только ссылки (и никогда не копируют весь объект).

Но вы можете легко написать реализацию общего назначения List, которая навязчива, создав необходимый интерфейс:

public interface IntrusiveListElement<E extends<IntrusiveListElement<E>> {
  public void setNext(E next);
  public E getNext();
  public void setPrev(E prev);
  public E getPrev();
}

public class IntrusiveList<E extends IntrusiveListElement<E>> implements List<E> {
  // implement your run-of-the-mill double-linked list here
}

Ваш класс элемента может выглядеть следующим образом:

public class MyBusinessElement implements IntrusiveListElement<MyBusinessElement> {
  private MyBusinessElement prev;
  private MyBusinessElement next;

  public void setNext(MyBusinessElement next) {
    this.next = next;
  }

      public MyBusinessElement getNext() {
    return next;
  }

  public void setPrev(MyBusinessElement prev) {
    this.prev = prev;
  }

  public MyBusinessElement getPrev() {
    return prev;
  }
}

Ответ 2

Есть ли какой-либо (хорошо реализованный) intrusive двойной связанный список классов (ов), доступных для Java?

Я не верю, что вы его найдете.

Мое понимание "навязчивого" заключается в том, что объект приложения, который должен быть сохранен в структуре данных, должен напрямую включать ( "вторгаться" ) следующие/предыдущие указатели в объект приложения.

В отличие от подхода "обертка указателя", где структура данных node содержит указатель на объект приложения, что не требует модификации объекта приложения. Навязчивый подход ряд преимуществ, включая исключение двойного разыменования (один раз для node и один раз для указателя node объект приложения), общий для подхода "обертка указателя".

Я не верю, что вы найдете интрузивную библиотеку данных для Java. Java не имеет С++-подобных шаблонов или множественного наследования, и он не просто поддерживает копирование по значению целого объекта. Из-за этого я не думаю, что есть способ в целом добавить поля next/prev для объекта Java.

Например, с учетом объекта приложения:

class Foo {
    int bar;
}

каким-то образом вам нужно будет добавить в него следующие/предыдущие поля и методы управления списками или их производные:

class Foo2 {
    int bar;
    Foo2 prev;
    Foo2 next;
}

Один из способов добавить эти поля, предоставив базовый класс этим полям для расширения этих классов приложений - это подход Boost. Однако этот подход очень ограничен в языке с одним наследованием, например Java.

Java-интерфейсы часто являются Java-ответами на множественное наследование, например. интерфейс, требующий методов getNext() и getPrev() классов приложений. Однако, если вам нужны интрузивные структуры данных по соображениям производительности, доступ к следующим/предыдущим полям с помощью метода может отрицательно повлиять на эти цели.

Генераторы Java также не расширяют классы необходимым образом.

Или я должен делать свои собственные?

Если его одно время для конкретного случая для тщательно оцененной потребности, обязательно сверните свой собственный. Если вы пытаетесь запустить общий для общего использования, я не уверен, что он того стоит.

Один очень грубый подход - это настраивать расширение класса приложения для добавления необходимых полей:

class Foo3 extends Foo {
    Foo3 prev;
    Foo3 next;
}

и повторите использование cut-n-paste, чтобы добавить методы управления списками. Однако я бы рекомендовал не использовать этот подход.

Soapbox

Вы не указываете, зачем нужна интрузивная структура данных. Возможно, у вас есть веские причины для его использования, но их трудно себе представить. Java сильно зависит от использования указателей объектов и пытается избежать их, как это было бы сложно.

Я почтительно предлагаю вам подумать:

  • Вы пытаетесь преждевременно оптимизировать,
  • Вы добавляете ненужную сложность для небольшой пользы.
  • Если скорость и управление памятью имеют первостепенное значение, используете ли вы правильный язык?

Ответ 3

если вы посмотрите на SDK-код, вы увидите, что LinkedList - это, по сути, список частной записи класса, содержащей следующие и предыдущие элементы. Итак, если вы включите MyClass в этот список, элементом списка будет запись с вашим объектом и ссылки для следующего и предыдущего элементов списка.

Итак, я думаю, что это навязчиво...

private static class Entry<E> {
    E element;
    Entry<E> next;
    Entry<E> previous;

    Entry(E element, Entry<E> next, Entry<E> previous) {
        this.element = element;
        this.next = next;
        this.previous = previous;
    }
}

Ответ 4

Что вы надеетесь получить с помощью навязчивого списка? Мне трудно думать, какие преимущества будут. Хорошо, есть тривиальная эффективность, что у вас есть только один объект вместо двух для каждого node. Но цена этого - большая потеря в гибкости и надежности. Например, теперь вы не можете иметь один и тот же объект в двух списках одновременно. Что произойдет, если вызывающий абонент получает экземпляр элемента списка и пытается изменить один из указателей? Что он вызывает, клонирует node и не обновляет указатели? Etc.

Дэйв Рэй утверждает, что с помощью навязчивого списка вы можете удалить node в постоянное время, потому что у вас уже есть указатели, и вам не нужно снова находить элемент. Правда, но предполагается, что вы где-то сохранили дескриптор node и теперь возвращаетесь к нему. С классом Java LinkedList в значительной степени единственный способ доступа к элементам - это перемещение списка с помощью Iterator или поиск определенного объекта. Если вы пройдете через Iterator, Iterator.remove будет удаляться в постоянное время. Так что, только если вы ищите, находите, а затем решаете удалить, что вы платите это наказание. Я думаю, что это было бы улучшением реализации LinkedList, если бы они кэшировали указатель на последний объект, который, как оказалось, улучшил производительность именно на этом этапе. Я предполагаю, что они этого не сделали, потому что это сделало бы удаление недетерминированным: вы могли бы иметь один и тот же объект в списке несколько раз - буквально один и тот же экземпляр или два объекта, сравнивающихся с равными - и контракт на удаление в настоящее время говорит, что он всегда удаляет первое вхождение. Если бы был кешированный указатель, было бы трудно сказать, было ли это первым, вторым или 42-м.

О, чтобы ответить на ваш вопрос: Нет, я не знаю о реализации с открытым исходным кодом. Но тогда, если бы мне нужен был собственный аромат связанного списка, я думаю, что просто брошу свой собственный. Предположительно, если стандартная Java одна не отвечает вашим потребностям, это должно означать, что у вас есть довольно специализированная потребность. В этом случае поиск готовой реализации, которая встречается с вашими специализированными потребностями, звучит как много неприятностей, и вы могли бы наверняка реализовать ее в том, что, день или два из работы? Вы, вероятно, потратили бы больше времени на поиск подходящего продукта, чем потребовалось бы, чтобы это сделать. Вероятно, вы уже потратили больше времени на чтение ответов на этот пост, чем это потребовалось бы, чтобы написать его.

Ответ 5

Вы взглянули на класс Deque? В javadoc: "Линейный набор, который поддерживает вставку и удаление элементов на обоих концах. Имя deque не подходит для" двойной очереди "и обычно произносится как" колода ". Он реализуется ArrayDeque, LinkedBlockingDeque и LinkedList.

Ответ 6

Семантика Java по дизайну навязчивая, они используют ссылки и не копируют. Вы просто хотите найти лучшую реализацию связанного списка, которая имеет java, LinkedList или что-то еще, и это будет навязчивым.

import java.util.LinkedList;

public class Foo {

  public static void main(String[] args) {
    String[] i = new String[1];
    i[0] = "FOO";
    String[] j = new String[1];
    j[0] = "BAZ";

    LinkedList<String[]> list = new LinkedList<String[]>();

    list.add(i);
    list.add(j);

    for (String[] x: list) {
      System.out.println(x[0]);
    }

    i[0] = "ZILCH";

    for (String[] x: list) {
      System.out.println(x[0]);
    }
  }
}

Здесь вывод, обратите внимание, как изменение я изменил значение в списке.

FOO
BAZ
ZILCH
BAZ