metodos - Múltiples índices para una colección Java: ¿la solución más básica?
imprimir arraylist java (14)
Así es como lo estoy logrando, ahora solo los métodos de poner, quitar y obtener funcionan para el descanso que necesita para anular los métodos deseados.
Ejemplo:
MultiKeyMap<MultiKeyMap.Key,String> map = new MultiKeyMap<>();
MultiKeyMap.Key key1 = map.generatePrimaryKey("keyA","keyB","keyC");
MultiKeyMap.Key key2 = map.generatePrimaryKey("keyD","keyE","keyF");
map.put(key1,"This is value 1");
map.put(key2,"This is value 2");
Log.i("MultiKeyMapDebug",map.get("keyA"));
Log.i("MultiKeyMapDebug",map.get("keyB"));
Log.i("MultiKeyMapDebug",map.get("keyC"));
Log.i("MultiKeyMapDebug",""+map.get("keyD"));
Log.i("MultiKeyMapDebug",""+map.get("keyE"));
Log.i("MultiKeyMapDebug",""+map.get("keyF"));
Salida:
MultiKeyMapDebug: This is value 1
MultiKeyMapDebug: This is value 1
MultiKeyMapDebug: This is value 1
MultiKeyMapDebug: This is value 2
MultiKeyMapDebug: This is value 2
MultiKeyMapDebug: This is value 2
MultiKeyMap.java:
/**
* Created by hsn on 11/04/17.
*/
public class MultiKeyMap<K extends MultiKeyMap.Key, V> extends HashMap<MultiKeyMap.Key, V> {
private Map<String, MultiKeyMap.Key> keyMap = new HashMap<>();
@Override
public V get(Object key) {
return super.get(keyMap.get(key));
}
@Override
public V put(MultiKeyMap.Key key, V value) {
List<String> keyArray = (List<String>) key;
for (String keyS : keyArray) {
keyMap.put(keyS, key);
}
return super.put(key, value);
}
@Override
public V remove(Object key) {
return super.remove(keyMap.get(key));
}
public Key generatePrimaryKey(String... keys) {
Key singleKey = new Key();
for (String key : keys) {
singleKey.add(key);
}
return singleKey;
}
public class Key extends ArrayList<String> {
}
}
Estoy buscando la solución más básica para crear múltiples índices en una Colección Java.
Funcionalidad requerida:
- Cuando se elimina un valor, todas las entradas de índice asociadas con ese valor deben eliminarse.
- La búsqueda de índice debe ser más rápida que la búsqueda lineal (al menos tan rápido como una TreeMap).
Condiciones laterales:
- Sin dependencias en bibliotecas grandes (como Lucene). No hay bibliotecas poco comunes o no bien probadas. Sin base de datos
- Una biblioteca como Apache Commons Collections etc. estaría bien.
- Aún mejor, si funciona solo con JavaSE (6.0).
- Editar: No hay solución auto-implementada (gracias por las respuestas que sugieren esto, es bueno tenerlos aquí para completar, pero ya tengo una solución muy similar a la de Jay) Cuando varias personas se enteran, que implementaron lo mismo, esto debería ser parte de una biblioteca común.
Por supuesto, podría escribir una clase que maneje múltiples mapas yo mismo (eso no es difícil, pero se siente como reinventar la rueda) . Así que me gustaría saber si se puede hacer sin, al mismo tiempo obtener un uso simple similar al uso de un solo índice indexado java.util.Map.
Gracias, Chris
Actualizar
Parece que no hemos encontrado nada. Me gustan todas tus respuestas: las versiones autodesarrolladas, los enlaces a bibliotecas similares a bases de datos.
Esto es lo que realmente quiero: tener la funcionalidad en (a) Colecciones de Apache Commons o (b) en Google Collections / Guava. O tal vez una muy buena alternativa.
¿Otras personas también pierden esta funcionalidad en estas bibliotecas? Proporcionan todo tipo de cosas como MultiMaps, MulitKeyMaps, BidiMaps, ... Siento que encajaría muy bien en esas bibliotecas, podría llamarse MultiIndexMap
. ¿Qué piensas?
Básicamente, sería posible una solución basada en múltiples hash maps, pero en este caso todos ellos deben actualizarse de forma manual. Se puede encontrar una solución integrada muy simple aquí: http://insidecoffe.blogspot.de/2013/04/indexable-hashmap-implementation.html
Cada índice será básicamente un Map
separado. Puede (y probablemente debería) abstraer esto detrás de una clase que gestiona las búsquedas, indización, actualizaciones y eliminaciones por usted. No sería difícil hacer esto de manera bastante genérica. Pero no, no hay una clase estándar estándar, aunque puede construirse fácilmente a partir de las clases de colecciones de Java.
Eche un vistazo a CQEngine (Collection Query Engine) , es un ajuste exacto para este tipo de requisito, ya que se basa en IndexedCollection
.
Consulte también preguntas relacionadas. ¿ Cómo consulta colecciones de objetos en Java (Criteria / SQL-like)? para más antecedentes.
He escrito una interfaz de tabla que incluye métodos como
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()
Nos gustaría incluirlo en una futura versión de Guava, pero no sé cuándo sucederá eso. http://code.google.com/p/guava-libraries/issues/detail?id=173
Lo primero que pensé fue crear una clase para lo que se indexaba, luego crear varios HashMap para contener los índices, con el mismo objeto agregado a cada uno de los HashMaps. Para un complemento, simplemente debe agregar el mismo objeto a cada HashMap. Una eliminación requeriría buscar en cada HashMap la referencia al objeto de destino. Si las eliminaciones necesitan ser rápidas, es posible que desee crear dos HashMaps para cada índice: uno para el índice a valor y el otro para valor a índice. Por supuesto, envolvería cualquier cosa que hagas en una clase con una interfaz claramente definida.
No parece que esto sería difícil. Si conoce los números y tipos de los índices y la clase del widget desde el principio, sería bastante fácil, como:
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 ...
}
}
Si desea que sea genérico y acepte cualquier objeto, tenga números variables y tipos de índices, etc., es un poco más complicado, pero no mucho.
Necesitas echar un vistazo a Boon. :)
http://rick-hightower.blogspot.com/2013/11/what-if-java-collections-and-java.html
Puede agregar n cantidad de índices de búsqueda e índices de búsqueda. También le permite consultar de manera eficiente las propiedades primitivas.
Aquí hay una toma de ejemplo de la wiki (yo soy el autor).
repoBuilder.primaryKey("ssn")
.searchIndex("firstName").searchIndex("lastName")
.searchIndex("salary").searchIndex("empNum", true)
.usePropertyForAccess(true);
Puede anular eso proporcionando un indicador verdadero como segundo argumento para searchIndex.
Observe que empNum es un índice exclusivo de búsqueda.
¿Qué pasaría si fuera fácil consultar un conjunto complejo de objetos Java en tiempo de ejecución? ¿Qué pasaría si hubiera una API que mantuviera sincronizados sus índices de objeto (realmente solo TreeMaps y HashMaps)? Bueno, entonces tendrías el repositorio de datos de Boon. Este artículo muestra cómo usar las herramientas de repo de datos de Boon para consultar objetos Java. Esta es la primera parte. Puede haber muchas, muchas partes. :) El repositorio de datos de Boon hace que las consultas basadas en índices en colecciones sean mucho más fáciles. Por qué Boon data repo
El repositorio de datos de Boon le permite tratar las colecciones de Java más como una base de datos al menos cuando se trata de consultar las colecciones. El repositorio de datos de Boon no es una base de datos en memoria, y no puede sustituir organizar sus objetos en estructuras de datos optimizadas para su aplicación. Si desea dedicar su tiempo a proporcionar valor al cliente y crear sus objetos y clases, y usar la API de Colecciones para sus estructuras de datos, entonces DataRepo es para usted. Esto no excluye la publicación de los libros de Knuth y la creación de una estructura de datos optimizada. Simplemente ayuda a mantener las cosas mundanas fáciles para que pueda pasar el tiempo haciendo las cosas difíciles. Nacido de la necesidad
Este proyecto surgió de una necesidad. Estaba trabajando en un proyecto que planeaba almacenar una gran colección de objetos de dominio en la memoria para la velocidad, y alguien hizo una pregunta muy importante que pasé por alto. ¿Cómo vamos a consultar esta información? Mi respuesta fue que usaremos Collections API y Streaming API. Luego traté de hacer esto ... Hmmm ... También me cansé de utilizar la API de transmisión JDK 8 en un gran conjunto de datos, y fue lento. (El repositorio de datos de Boon funciona con JDK7 y JDK8). Fue una búsqueda / filtro lineal. Esto es por diseño, pero por lo que estaba haciendo, no funcionó. Necesitaba índices para admitir consultas arbitrarias. Repo de datos de Boon aumenta la API de transmisión.
El repositorio de datos de Boon no se esfuerza por reemplazar la API de transmisión JDK 8, y de hecho funciona bien con ella. Repo de datos de Boon le permite crear colecciones indexadas. Los índices pueden ser cualquier cosa (es conectable). En este momento, los índices de repositorios de datos de Boon se basan en ConcurrentHashMap y ConcurrentSkipListMap. Por diseño, el repositorio de datos de Boon funciona con bibliotecas de colecciones estándar. No hay un plan para crear un conjunto de colecciones personalizadas. Uno debería ser capaz de conectar Guava, Árboles Concurrentes o Trove si uno desea hacerlo. Proporciona una API simplificada para hacerlo. Permite la búsqueda lineal para una sensación de finalización, pero recomiendo usarlo principalmente para usar índices y luego usar la API de transmisión para el resto (para seguridad y velocidad de tipo).
escabullirse antes del paso a paso
Supongamos que tiene un método que crea 200,000 objetos de empleados como este:
List<Employee> employees = TestHelper.createMetricTonOfEmployees(200_000);
Entonces ahora tenemos 200,000 empleados. Vamos a buscarlos ...
Primer ajuste de los empleados en una consulta de búsqueda:
employees = query(employees);
Ahora busca:
List<Employee> results = query(employees, eq("firstName", firstName));
Entonces, ¿cuál es la principal diferencia entre la API anterior y la corriente?
employees.stream().filter(emp -> emp.getFirstName().equals(firstName)
¡Aproximadamente un factor de 20,000% más rápido para usar DataRepo de Boon! Ah, el poder de HashMaps y TreeMaps. :) Hay una API que se parece a tus colecciones integradas. También hay una API que se parece más a un objeto DAO o un objeto Repo.
Una consulta simple con el objeto Repo / DAO se ve así:
List<Employee> employees = repo.query(eq("firstName", "Diana"));
Una consulta más complicada se vería así:
List<Employee> employees = repo.query(
and(eq("firstName", "Diana"), eq("lastName", "Smith"), eq("ssn", "21785999")));
O esto:
List<Employee> employees = repo.query(
and(startsWith("firstName", "Bob"), eq("lastName", "Smith"), lte("salary", 200_000),
gte("salary", 190_000)));
O incluso esto:
List<Employee> employees = repo.query(
and(startsWith("firstName", "Bob"), eq("lastName", "Smith"), between("salary", 190_000, 200_000)));
O si desea usar la API de transmisión JDK 8, esto no está en su contra:
int sum = repo.query(eq("lastName", "Smith")).stream().filter(emp -> emp.getSalary()>50_000)
.mapToInt(b -> b.getSalary())
.sum();
Lo anterior sería mucho más rápido si la cantidad de empleados fuera bastante grande. Limitaría a los empleados cuyo nombre comenzó con Smith y tuvo un salario superior a 50,000. Supongamos que tiene 100.000 empleados y solo 50 con el nombre de Smith, por lo que ahora reduce a 50 rápidamente al usar el índice que efectivamente saca a 50 empleados de cada 100.000, luego filtramos solo 50 en lugar de 100.000.
Aquí hay una ejecución de referencia desde el repositorio de datos de una búsqueda lineal frente a una búsqueda indexada en nano segundos:
Name index Time 218
Name linear Time 3709120
Name index Time 213
Name linear Time 3606171
Name index Time 219
Name linear Time 3528839
Alguien me dijo recientemente: "Pero con la API de transmisión, puedes ejecutar el filtro en parralel).
Veamos cómo se sostiene la matemática:
3,528,839 / 16 threads vs. 219
201,802 vs. 219 (nano-seconds).
Los índices ganan, pero fue un final de foto. ¡NO! :)
Fue solo 9.500% más rápido en lugar de 40.000% más rápido. Tan cerca.....
Agregué algunas características más. Ellos hacen un uso intensivo de los índices. :)
repo.updateByFilter (values (value ("firstName", "Di")), y (eq ("firstName", "Diana"), eq ("lastName", "Smith"), eq ("ssn", "21785999 ")));
Lo anterior sería equivalente a
ACTUALIZACIÓN Empleado e SET e.firstName = ''Di'' WHERE e.firstName = ''Diana'' y e.lastName = ''Smith'' y e.ssn = ''21785999''
Esto le permite establecer múltiples campos a la vez en múltiples registros, de modo que si estaba haciendo una actualización masiva.
Hay métodos sobrecargados para todos los tipos básicos, de modo que si tiene un valor para actualizar en cada elemento devuelto desde un Filtro:
repo.updateByFilter("firstName", "Di",
and( eq("firstName", "Diana"),
eq("lastName", "Smith"),
eq("ssn", "21785999") ) );
Aquí hay algunas capacidades básicas de selección:
List <Map<String, Object>> list =
repo.query(selects(select("firstName")), eq("lastName", "Hightower"));
Puede tener tantas selecciones como desee. También puede traer la lista de vuelta ordenada:
List <Map<String, Object>> list =
repo.sortedQuery("firstName",selects(select("firstName")),
eq("lastName", "Hightower"));
Puede seleccionar propiedades de propiedades relacionadas (es decir, employee.department.name).
List <Map<String, Object>> list = repo.query(
selects(select("department", "name")),
eq("lastName", "Hightower"));
assertEquals("engineering", list.get(0).get("department.name"));
Lo anterior trataría de usar los campos de las clases. Si desea utilizar las propiedades reales (emp.getFoo () frente a emp.foo), entonces necesita usar selectPropertyPath.
List <Map<String, Object>> list = repo.query(
selects(selectPropPath("department", "name")),
eq("lastName", "Hightower"));
Tenga en cuenta que select ("departamento", "nombre") es mucho más rápido que selectPropPath ("departamento", "nombre"), lo que podría importar en un ciclo cerrado.
De forma predeterminada, todos los índices de búsqueda y de búsqueda permiten duplicados (excepto el índice de clave principal).
repoBuilder.primaryKey("ssn")
.searchIndex("firstName").searchIndex("lastName")
.searchIndex("salary").searchIndex("empNum", true)
.usePropertyForAccess(true);
Puede anular eso proporcionando un indicador verdadero como segundo argumento para searchIndex.
Observe que empNum es un índice exclusivo de búsqueda.
Si lo prefiere o necesita, puede recuperar incluso búsquedas simples como mapas:
List<Map<String, Object>> employees = repo.queryAsMaps(eq("firstName", "Diana"));
No estoy seguro de si esto es una característica o un error. Mi pensamiento fue que una vez que se trata de datos, debe presentarlos de una manera que no vincule a los consumidores de datos con su API real. Tener un Mapa de Cadenas / tipos básicos parece ser una forma de lograr esto. Tenga en cuenta que el objeto para mapear la conversión es profundo como en:
System.out.println(employees.get(0).get("department"));
Rendimientos:
{class=Department, name=engineering}
Esto puede ser útil para la depuración y consultas ad hoc para herramientas. Estoy considerando agregar soporte para convertir fácilmente a una cadena JSON.
Se agregó la capacidad de consultar propiedades de colección. Esto debería funcionar con colecciones y matrices tan profundamente anidadas como desee. ¡Lea eso de nuevo porque fue un verdadero MF para implementar!
List <Map<String, Object>> list = repo.query(
selects(select("tags", "metas", "metas2", "metas3", "name3")),
eq("lastName", "Hightower"));
print("list", list);
assertEquals("3tag1", idx(list.get(0).get("tags.metas.metas2.metas3.name3"), 0));
La impresión de lo anterior se ve así:
list [{tags.metas.metas2.metas3.name3=[3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3]},
...
Creé varias clases de relación para probar esto:
public class Employee {
List <Tag> tags = new ArrayList<>();
{
tags.add(new Tag("tag1"));
tags.add(new Tag("tag2"));
tags.add(new Tag("tag3"));
}
...
public class Tag {
...
List<Meta> metas = new ArrayList<>();
{
metas.add(new Meta("mtag1"));
metas.add(new Meta("mtag2"));
metas.add(new Meta("mtag3"));
}
}
public class Meta {
...
List<Meta2> metas2 = new ArrayList<>();
{
metas2.add(new Meta2("2tag1"));
metas2.add(new Meta2("2tag2"));
metas2.add(new Meta2("2tag3"));
}
}
...
public class Meta2 {
List<Meta3> metas3 = new ArrayList<>();
{
metas3.add(new Meta3("3tag1"));
metas3.add(new Meta3("3tag2"));
metas3.add(new Meta3("3tag3"));
}
public class Meta3 {
...
También puede buscar por tipo:
List<Employee> results = sortedQuery(queryableList, "firstName", typeOf("SalesEmployee"));
assertEquals(1, results.size());
assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
Lo anterior encuentra a todos los empleados con el nombre de clase simple de SalesEmployee. También funciona con nombre completo de clase como en:
List<Employee> results = sortedQuery(queryableList, "firstName", typeOf("SalesEmployee"));
assertEquals(1, results.size());
assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
Puede buscar por la clase real también:
List<Employee> results = sortedQuery(queryableList, "firstName", instanceOf(SalesEmployee.class));
assertEquals(1, results.size());
assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
También puede consultar clases que implementen ciertas interfaces:
List<Employee> results = sortedQuery(queryableList, "firstName",
implementsInterface(Comparable.class));
assertEquals(1, results.size());
assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
También puede indexar campos / propiedades anidados y pueden ser campos de recopilación o campos de propiedad no de colección tan anidados como desee:
/* Create a repo, and decide what to index. */
RepoBuilder repoBuilder = RepoBuilder.getInstance();
/* Look at the nestedIndex. */
repoBuilder.primaryKey("id")
.searchIndex("firstName").searchIndex("lastName")
.searchIndex("salary").uniqueSearchIndex("empNum")
.nestedIndex("tags", "metas", "metas2", "name2");
Más tarde puede usar el índice anidado para buscar.
List<Map<String, Object>> list = repo.query(
selects(select("tags", "metas", "metas2", "name2")),
eqNested("2tag1", "tags", "metas", "metas2", "name2"));
La forma segura de usar el indexado es usar eqNested. Puedes usar eq, gt, gte, etc. si tienes el índice así:
List<Map<String, Object>> list = repo.query(
selects(select("tags", "metas", "metas2", "name2")),
eq("tags.metas.metas2.name2", "2tag1"));
También puede agregar soporte para subclases
List<Employee> queryableList = $q(h_list, Employee.class, SalesEmployee.class,
HourlyEmployee.class);
List<Employee> results = sortedQuery(queryableList, "firstName", eq("commissionRate", 1));
assertEquals(1, results.size());
assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
results = sortedQuery(queryableList, "firstName", eq("weeklyHours", 40));
assertEquals(1, results.size());
assertEquals("HourlyEmployee", results.get(0).getClass().getSimpleName());
El repositorio de datos tiene una característica similar en su método DataRepoBuilder.build (...) para especificar subclases. Esto le permite a los campos de búsqueda sin formato formar subclases y clases en el mismo repositorio o colección de búsqueda.
No estoy seguro de entender la pregunta, pero creo que lo que estás pidiendo es varias formas de mapear diferentes claves únicas de los valores y una limpieza adecuada cuando un valor desaparece.
Veo que no quieres rodar el tuyo, pero hay una composición bastante simple de mapa y multimapa (utilicé el multimapa Guava a continuación, pero el de Apache debería funcionar también) para hacer lo que quieras. Tengo una solución rápida y sucia abajo (omití los constructores, ya que eso depende del tipo de mapa / multimapa subyacente que quieras usar):
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();
}
}
la eliminación es O (número de claves), pero todo lo demás está en el mismo orden que una implementación de mapa típica (una escala constante adicional, ya que también tiene que agregar cosas al revés).
Acabo de utilizar claves Object
(debería estar bien con implementaciones apropiadas de equals()
y hashCode()
y distinción de teclas), pero también podría tener un tipo de clave más específico.
Si quiere múltiples índices en sus datos, puede crear y mantener múltiples mapas de hash o usar una biblioteca como Data Store:
https://github.com/jparams/data-store
Ejemplo:
Store<Person> store = new MemoryStore<>() ;
store.add(new Person(1, "Ed", 3));
store.add(new Person(2, "Fred", 7));
store.add(new Person(3, "Freda", 5));
store.index("name", Person::getName);
Person person = store.getFirst("name", "Ed");
Con data store puede crear índices insensibles a mayúsculas y todo tipo de cosas interesantes. Vale la pena echarle un vistazo.
Su principal objetivo parece ser que eliminará el objeto de todos los índices cuando lo elimine de uno.
El enfoque más simple será agregar otra capa de direccionamiento indirecto: usted almacena su objeto real en un Map<Long,Value>
, y utiliza un mapa bidireccional (que encontrará en Jakarta Commons y probablemente en el Código de Google) para sus índices como Map<Key,Long>
. Cuando elimina una entrada de un índice en particular, tomará el valor Long
de ese índice y lo usará para eliminar las entradas correspondientes del mapa principal y de los otros índices.
Una alternativa a BIDIMap es definir sus mapas de "índice" como Map<Key,WeakReference<Long>>
; sin embargo, esto requerirá que implemente una ReferenceQueue
para la limpieza.
Otra alternativa es crear un objeto clave que pueda tomar una tupla arbitraria, definir su método equals()
para que coincida con cualquier elemento de la tupla y usar eso con un TreeMap
. No puede usar un HashMap
, porque no podrá calcular un código hash basado en un solo elemento de la tupla.
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()
}
}
Tiene muchos requisitos muy estrictos que parecen ser muy particulares a sus necesidades. La mayoría de las cosas que dices que no son viables se deben a que muchas personas tienen exactamente las mismas necesidades, lo que básicamente define un motor de base de datos básico. Es por eso que son bibliotecas "grandes". Usted dice "sin base de datos", pero en su esencia cada sistema de indexación es una "base de datos" de términos y documentos. Yo diría que una Colección es una "base de datos". Yo diría que eche un vistazo a Space4J .
Diría que si no encuentra lo que está buscando, inicie un proyecto en GitHub y siga con la codificación y comparta los resultados.
Use Tables Prefuse . Admiten tantos índices como desee, son rápidos (los índices son TreeMaps) y tienen buenas opciones de filtrado (¿filtros booleanos? ¡No hay problema!). No se requiere base de datos, probado con grandes conjuntos de datos en muchas aplicaciones de visualización de información.
En su forma original, no son tan convenientes como los contenedores estándar (es necesario tratar con filas y columnas), pero seguramente se puede escribir una pequeña envoltura alrededor de eso. Además, se conectan muy bien a los componentes de la interfaz de usuario, como los JTables de Swing.
echemos un vistazo al proyecto http://code.google.com/p/multiindexcontainer/wiki/MainPage Esta es una forma generalizada de cómo usar los mapas para los buscadores de JavaBean y realizar búsquedas sobre los valores indexados. Creo que esto es lo que estás buscando. Hagamos un intento.
Colecciones de Google LinkedListMultimap
Sobre su primer requisito
- Cuando se elimina un valor, todas las entradas de índice asociadas con ese valor deben eliminarse.
Creo que no hay una biblioteca ni un Ayudante que lo soporte.
Así es como lo hice usando 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
Para obtener su primer requisito, un Ayudante puede jugar un buen trabajo
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
Las colecciones de Google son
- ligero
- Compatible con Joshua Block (Java efectivo)
- Buenas funciones como ImmutableList, ImmutableMap, etc.