Я ищу самое основное решение создать несколько индексов на Наборе Java.
Необходимая функциональность:
Условия стороны:
Конечно, я мог записать класс, который управляет несколькими Картами сам (это не твердо, но как изобретать велосипед). Таким образом, я хотел бы знать, если это может обойтись без - все еще получение простого использования, подобного использованию сингла, индексировало java.util. Карта.
Спасибо, Chris
Выглядит очень, как будто мы ничего не нашли. Мне нравятся все Ваши ответы - сам разработанные версии, ссылки на подобные базе данных библиотеки.
Вот то, что я действительно хочу: иметь функциональность в (a) Apache Наборы палаты общин или (b) в Google Collections/Guava. Или возможно очень хорошая альтернатива.
Другие люди пропускают эту функциональность в этих библиотеках, также? Они действительно обеспечивают все виды вещей как MultiMaps, MulitKeyMaps, BidiMaps... Я чувствую, это поместилось бы в те библиотеки приятно - это можно было назвать MultiIndexMap
. Что Вы думаете?
Каждый индекс в основном представляет собой отдельную карту
. Вы можете (и, вероятно, должны) абстрагироваться от этого класса, который управляет поиском, индексированием, обновлением и удалением для вас. Это было бы несложно сделать в общих чертах. Но нет, для этого нет стандартного готового класса, хотя его можно легко построить из классов Java Collections.
Я не уверен, что понимаю вопрос, но думаю, что вы просите о нескольких способах сопоставления разных уникальных ключей со значениями и соответствующей очистке, когда значение исчезает.
Я вижу, что вы не хотите сворачивать свою собственную, но есть достаточно простая композиция из карты и мульти-карты (ниже я использовал мульти-карту 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 ()
и различие ключей) - но вы также можете иметь более конкретный тип ключа.
Я написал интерфейс таблицы, который включает такие методы, как
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
Моей первой мыслью было бы создать класс для индексируемого объекта, затем создать несколько 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 ...
}
}
Если вы хотите сделать его общим и принимать любой объект, иметь переменное количество и типы индексов и т.д., это немного сложнее, но не намного.
Используйте Prefuse Tables . Они поддерживают сколько угодно индексов, работают быстро (индексы - это TreeMaps) и имеют удобные параметры фильтрации (логические фильтры? Нет проблем!). База данных не требуется, протестировано с большими наборами данных во многих приложениях визуализации информации.
В исходном виде они не так удобны, как стандартные контейнеры (вам нужно иметь дело со строками и столбцами), но вы наверняка можете написать небольшую оболочку вокруг этого. Кроме того, они прекрасно подключаются к компонентам пользовательского интерфейса, таким как Swing JTables.
Коллекции 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
У вас есть множество действительно жестких требований, которые, похоже, очень специфичны для ваших нужд. Большинство из того, что вы говорите, нежизнеспособны, потому что у многих людей есть те же точные потребности, которые в основном определяют базовый движок базы данных. Вот почему это «большие» библиотеки. Вы говорите «нет базы данных», но по своей сути каждая система индексирования представляет собой «базу данных» терминов и документов. Я бы сказал, что Коллекция - это «база данных». Я бы посоветовал взглянуть на Space4J .
Я бы посоветовал, если вы не найдете то, что ищете, начните проект на GitHub и продолжайте писать его самостоятельно и делитесь результатами.
Похоже, ваша главная цель состоит в том, чтобы удалить объект из всех индексов, когда вы удалите его из одного.
Самый простой подход - добавить еще один уровень косвенности: вы сохраняете свой фактический объект в 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()
}
}