Несколько индексов для Набора Java - самое основное решение?

Я ищу самое основное решение создать несколько индексов на Наборе Java.

Необходимая функциональность:

  • Когда Значение удалено, все элементы индекса, связанные с тем значением, должны быть удалены.
  • Индексный поиск должен быть быстрее, чем линейный поиск (по крайней мере, с такой скоростью, как TreeMap).

Условия стороны:

  • Никакие зависимости от большого (как Lucene) библиотеки. Нет редкий или не хорошо протестированные библиотеки. Никакая база данных.
  • Библиотека как Apache Наборы палаты общин и т.д. была бы в порядке.
  • Еще лучше, если это работает с JavaSE (6.0) один.
  • Править: Никакое самоиспользуемое решение (благодарит за ответы, предлагающие это - хорошо иметь их здесь для полноты, но у меня уже есть решение, очень похожее на Jay) Каждый раз, когда несколько человек узнают, что они реализовали то же самое, это должно быть частью некоторой общей библиотеки.

Конечно, я мог записать класс, который управляет несколькими Картами сам (это не твердо, но как изобретать велосипед). Таким образом, я хотел бы знать, если это может обойтись без - все еще получение простого использования, подобного использованию сингла, индексировало java.util. Карта.

Спасибо, Chris

Обновление

Выглядит очень, как будто мы ничего не нашли. Мне нравятся все Ваши ответы - сам разработанные версии, ссылки на подобные базе данных библиотеки.

Вот то, что я действительно хочу: иметь функциональность в (a) Apache Наборы палаты общин или (b) в Google Collections/Guava. Или возможно очень хорошая альтернатива.

Другие люди пропускают эту функциональность в этих библиотеках, также? Они действительно обеспечивают все виды вещей как MultiMaps, MulitKeyMaps, BidiMaps... Я чувствую, это поместилось бы в те библиотеки приятно - это можно было назвать MultiIndexMap. Что Вы думаете?

48
задан Chris Lercher 2 April 2010 в 15:01
поделиться

8 ответов

Каждый индекс в основном представляет собой отдельную карту . Вы можете (и, вероятно, должны) абстрагироваться от этого класса, который управляет поиском, индексированием, обновлением и удалением для вас. Это было бы несложно сделать в общих чертах. Но нет, для этого нет стандартного готового класса, хотя его можно легко построить из классов Java Collections.

20
ответ дан 26 November 2019 в 19:01
поделиться

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

Я вижу, что вы не хотите сворачивать свою собственную, но есть достаточно простая композиция из карты и мульти-карты (ниже я использовал мульти-карту Guava, но Apache тоже должен работать), чтобы делать то, что вы хотите. У меня есть быстрое и грязное решение ниже (пропущены конструкторы, поскольку это зависит от того, какой тип базовой карты / мульти-карты вы хотите использовать):

package edu.cap10.common.collect;

import java.util.Collection;
import java.util.Map;

import com.google.common.collect.ForwardingMap;
import com.google.common.collect.Multimap;

public class MIndexLookupMap<T> extends ForwardingMap<Object,T>{

    Map<Object,T> delegate;
    Multimap<T,Object> reverse;

    @Override protected Map<Object, T> delegate() { return delegate; }

    @Override public void clear() {
        delegate.clear();
        reverse.clear();
    }

    @Override public boolean containsValue(Object value) { return reverse.containsKey(value); }

    @Override public T put(Object key, T value) {
        if (containsKey(key) && !get(key).equals(value)) reverse.remove(get(key), key); 
        reverse.put(value, key);
        return delegate.put(key, value);
    }

    @Override public void putAll(Map<? extends Object, ? extends T> m) {
        for (Entry<? extends Object,? extends T> e : m.entrySet()) put(e.getKey(),e.getValue());
    }

    public T remove(Object key) {
        T result = delegate.remove(key);
        reverse.remove(result, key);
        return result;
    }

    public void removeValue(T value) {
        for (Object key : reverse.removeAll(value)) delegate.remove(key);
    }

    public Collection<T> values() {
        return reverse.keySet();
    }   

}

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

Я только что использовал ключи объекта (должно быть хорошо с соответствующими реализациями equals () и hashCode () и различие ключей) - но вы также можете иметь более конкретный тип ключа.

0
ответ дан 26 November 2019 в 19:01
поделиться

Я написал интерфейс таблицы, который включает такие методы, как

V put(R rowKey, C columnKey, V value) 
V get(Object rowKey, Object columnKey) 
Map<R,V> column(C columnKey) 
Set<C> columnKeySet()
Map<C,V> row(R rowKey)
Set<R> rowKeySet()
Set<Table.Cell<R,C,V>> cellSet()

. Мы хотели бы включить его в будущую версию Guava, но я не знаю, когда это произойдет. http://code.google.com/p/guava-libraries/issues/detail?id=173

2
ответ дан 26 November 2019 в 19:01
поделиться

Моей первой мыслью было бы создать класс для индексируемого объекта, затем создать несколько HashMap для хранения индексов, с добавлением одного и того же объекта в каждый из HashMap. Для добавления вы просто добавляете один и тот же объект в каждую HashMap. Удаление потребует поиска в каждой HashMap ссылки на целевой объект. Если удаление должно быть быстрым, вы можете создать две HashMap для каждого индекса: одну для индекса к значению, а другую для значения к индексу. Конечно, я бы обернул все, что вы делаете, в класс с четко определенным интерфейсом.

Не похоже, что это будет сложно. Если вы заранее знаете номера и типы индексов и класс виджета, это будет довольно просто, например:

public class MultiIndex
{
  HashMap<String,Widget> index1=new HashMap<String,Widget>();
  HashMap<String,Widget> index2=new HashMap<String,Widget>();
  HashMap<Integer,Widget> index3=new HashMap<Integer,Widget>();

  public void add(String index1Value, String index2Value, Integer index3Value, Widget widget)
  {
    index1.put(index1Value, widget);
    index2.put(index2Value, widget);
    index3.put(index3Value, widget);
  }
  public void delete(Widget widget)
  {
    Iterator i=index1.keySet().iterator(); 
    while (i.hasNext())
    {
      String index1Value=(String)i.next();
      Widget gotWidget=(Widget) index1.get(index1Value);
      if (gotWidget.equals(widget))
        i.remove();
    }
    ... similarly for other indexes ...
  }
  public Widget getByIndex1(String index1Value)
  {
    return index1.get(index1Value);
  }
  ... similarly for other indexes ...

  }
}

Если вы хотите сделать его общим и принимать любой объект, иметь переменное количество и типы индексов и т.д., это немного сложнее, но не намного.

8
ответ дан 26 November 2019 в 19:01
поделиться

Используйте Prefuse Tables . Они поддерживают сколько угодно индексов, работают быстро (индексы - это TreeMaps) и имеют удобные параметры фильтрации (логические фильтры? Нет проблем!). База данных не требуется, протестировано с большими наборами данных во многих приложениях визуализации информации.

В исходном виде они не так удобны, как стандартные контейнеры (вам нужно иметь дело со строками и столбцами), но вы наверняка можете написать небольшую оболочку вокруг этого. Кроме того, они прекрасно подключаются к компонентам пользовательского интерфейса, таким как Swing JTables.

1
ответ дан 26 November 2019 в 19:01
поделиться

Коллекции Google LinkedListMultimap

О вашем первом требовании

  • При удалении значения все записи индекса, связанные с этим значением, должны быть удалены.

Думаю, это не поддерживает ни библиотека, ни помощник.

Вот как я сделал это с помощью LinkedListMultimap

Multimap<Integer, String> multimap = LinkedListMultimap.create();

// Three duplicates entries
multimap.put(1, "A");
multimap.put(2, "B");
multimap.put(1, "A");
multimap.put(4, "C");
multimap.put(1, "A");

System.out.println(multimap.size()); // outputs 5

Чтобы выполнить ваше первое требование, Помощник может неплохо справиться

public static <K, V> void removeAllIndexEntriesAssociatedWith(Multimap<K, V> multimap, V value) {
    Collection<Map.Entry<K, V>> eCollection = multimap.entries();
    for (Map.Entry<K, V> entry : eCollection)
        if(entry.getValue().equals(value))
            eCollection.remove(entry);
}

...

removeAllIndexEntriesAssociatedWith(multimap, "A");

System.out.println(multimap.size()); // outputs 2

Коллекции Google

  • легковесны
  • Поддерживается Джошуа Блок (эффективная Java)
  • Хорошие функции, такие как ImmutableList, ImmutableMap и т. Д.
4
ответ дан 26 November 2019 в 19:01
поделиться

У вас есть множество действительно жестких требований, которые, похоже, очень специфичны для ваших нужд. Большинство из того, что вы говорите, нежизнеспособны, потому что у многих людей есть те же точные потребности, которые в основном определяют базовый движок базы данных. Вот почему это «большие» библиотеки. Вы говорите «нет базы данных», но по своей сути каждая система индексирования представляет собой «базу данных» терминов и документов. Я бы сказал, что Коллекция - это «база данных». Я бы посоветовал взглянуть на Space4J .

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

5
ответ дан 26 November 2019 в 19:01
поделиться

Похоже, ваша главная цель состоит в том, чтобы удалить объект из всех индексов, когда вы удалите его из одного.

Самый простой подход - добавить еще один уровень косвенности: вы сохраняете свой фактический объект в Map и используете двунаправленную карту (которую вы найдете в Jakarta Commons и возможно, Google Code) для ваших индексов как Map . Когда вы удаляете запись из определенного индекса, вы берете значение Long из этого индекса и используете его для удаления соответствующих записей из основной карты и других индексов.

Альтернативой BIDIMap является определение ваших «индексных» карт как Map > ; однако для этого потребуется реализовать ReferenceQueue для очистки.


Другой альтернативой является создание ключевого объекта, который может принимать произвольный кортеж, определить его метод equals () для сопоставления с любым элементом в кортеже и использовать его с TreeMap ]. Вы не можете использовать HashMap , потому что вы не сможете вычислить хэш-код на основе только одного элемента кортежа.

public class MultiKey
implements Comparable<Object>
{
   private Comparable<?>[] _keys;
   private Comparable _matchKey;
   private int _matchPosition;

   /**
    *  This constructor is for inserting values into the map.
    */
   public MultiKey(Comparable<?>... keys)
   {
      // yes, this is making the object dependent on externally-changable
      // data; if you're paranoid, copy the array
      _keys = keys;
   }


   /**
    *  This constructor is for map probes.
    */
   public MultiKey(Comparable key, int position)
   {
      _matchKey = key;
      _matchPosition = position;
   }


   @Override
   public boolean equals(Object obj)
   {
      // verify that obj != null and is castable to MultiKey
      if (_keys != null)
      {
         // check every element
      }
      else
      {
         // check single element
      }
   }


   public int compareTo(Object o)
   {
      // follow same pattern as equals()
   }
}
2
ответ дан 26 November 2019 в 19:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: