Java集合的多个索引-最基本的解决方案?
我正在寻找在Java集合上创建多个索引的最基本解决方案 所需功能:Java集合的多个索引-最基本的解决方案?,java,collections,indexing,Java,Collections,Indexing,我正在寻找在Java集合上创建多个索引的最基本解决方案 所需功能: 删除某个值时,必须删除与该值关联的所有索引项 索引查找必须比线性搜索快(至少与树映射一样快) 附带条件: 不依赖于大型(如Lucene)库。没有不常见或未经良好测试的库。没有数据库 像ApacheCommons Collections之类的库就可以了 如果它能单独与JavaSE(6.0)一起工作,那就更好了 编辑:没有自我实现的解决方案(感谢您的回答,这是一个很好的建议,但是我已经有了一个与Jay非常相似的解决方案),每当
- 删除某个值时,必须删除与该值关联的所有索引项
- 索引查找必须比线性搜索快(至少与树映射一样快)
- 不依赖于大型(如Lucene)库。没有不常见或未经良好测试的库。没有数据库
- 像ApacheCommons Collections之类的库就可以了
- 如果它能单独与JavaSE(6.0)一起工作,那就更好了
- 编辑:没有自我实现的解决方案(感谢您的回答,这是一个很好的建议,但是我已经有了一个与Jay非常相似的解决方案),每当有人发现他们实现了相同的东西时,这应该是一些公共库的一部分
其他人也会错过这些库中的这个功能吗?他们确实提供了各种各样的东西,比如多重映射、多重键映射、BidiMaps等等。。。我觉得,它很适合那些库——它可以被称为
MultiIndexMap
。你认为呢?每个索引基本上都是一个独立的地图。您可以(也可能应该)将其抽象为一个类,该类为您管理搜索、索引、更新和删除。一般来说,这样做并不难。但是没有,没有标准的开箱即用类,尽管它可以很容易地从Java Collections类构建。我的第一个想法是为被索引的对象创建一个类,然后创建多个HashMap来保存索引,将相同的对象添加到每个HashMap中。对于add,只需将相同的对象添加到每个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 ...
}
}
公共类多索引
{
HashMap index1=新的HashMap();
HashMap index2=新的HashMap();
HashMap index3=新的HashMap();
public void add(字符串index1Value、字符串index2Value、整数index3Value、小部件)
{
index1.put(index1Value,小部件);
index2.put(index2Value,小部件);
index3.put(index3Value,小部件);
}
公共无效删除(小部件)
{
迭代器i=index1.keySet().Iterator();
while(i.hasNext())
{
字符串index1Value=(字符串)i.next();
Widget-gotWidget=(Widget)index1.get(index1Value);
if(gotWidget.equals(widget))
i、 删除();
}
…对于其他索引也是如此。。。
}
公共小部件getByIndex1(字符串index1Value)
{
返回index1.get(index1Value);
}
…对于其他索引也是如此。。。
}
}
如果您想使它成为泛型并接受任何对象、具有可变数量和类型的索引等,它会稍微复杂一点,但不会太复杂。我编写了一个表接口,其中包括以下方法
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()
V put(R行键、C列键、V值)
V get(对象行键、对象列键)
映射列(C列键)
Set columnKeySet()
地图行(R行键)
设置行键集()
集合单元集()
我们想在未来的番石榴版本中加入它,但我不知道什么时候会发生。
关于你的第一个要求
- 删除某个值时,必须删除与该值关联的所有索引项
我认为既没有图书馆也没有助手支持它
下面是我如何使用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
谷歌收藏是
- 轻量级
- 由Joshua Block(有效Java)支持
- 不错的特性,如ImmutableList、ImmutableMap等等
您有很多非常严格的要求,但这些要求似乎对您的需求非常特殊。你所说的大多数事情都是不可行的,因为很多人都有相同的需求,基本上定义了一个基本的数据库引擎。这就是为什么它们是“大型”图书馆。你说“没有数据库”,但每个索引系统的核心都是术语和文档的“数据库”。我认为收藏是一个“数据库”。我想说看看
我想说,如果你找不到你想要的东西,在GitHub上启动一个项目,然后自己编写代码并分享结果。我不确定我是否理解这个问题,但我认为你要求的是多种方法,从不同的、唯一的键映射到值,并在值消失时进行适当的清理
我知道你不想自己动手,但是有一个足够简单的map和multimap的组合(我在下面使用了Guava multimap,但是Apache的multimap应该也可以)来做你想做的事情。下面我有一个快速而肮脏的解决方案(跳过了构造函数,因为这取决于您想要使用哪种类型的底层映射/多重映射):
包edu.cap10.common.collect;
导入java.util.Collection;
导入java.util.Map;
导入com.google.c
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
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();
}
}
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()
}
}
repoBuilder.primaryKey("ssn")
.searchIndex("firstName").searchIndex("lastName")
.searchIndex("salary").searchIndex("empNum", true)
.usePropertyForAccess(true);
List<Employee> employees = TestHelper.createMetricTonOfEmployees(200_000);
employees = query(employees);
List<Employee> results = query(employees, eq("firstName", firstName));
employees.stream().filter(emp -> emp.getFirstName().equals(firstName)
List<Employee> employees = repo.query(eq("firstName", "Diana"));
List<Employee> employees = repo.query(
and(eq("firstName", "Diana"), eq("lastName", "Smith"), eq("ssn", "21785999")));
List<Employee> employees = repo.query(
and(startsWith("firstName", "Bob"), eq("lastName", "Smith"), lte("salary", 200_000),
gte("salary", 190_000)));
List<Employee> employees = repo.query(
and(startsWith("firstName", "Bob"), eq("lastName", "Smith"), between("salary", 190_000, 200_000)));
int sum = repo.query(eq("lastName", "Smith")).stream().filter(emp -> emp.getSalary()>50_000)
.mapToInt(b -> b.getSalary())
.sum();
Name index Time 218
Name linear Time 3709120
Name index Time 213
Name linear Time 3606171
Name index Time 219
Name linear Time 3528839
3,528,839 / 16 threads vs. 219
201,802 vs. 219 (nano-seconds).
repo.updateByFilter("firstName", "Di",
and( eq("firstName", "Diana"),
eq("lastName", "Smith"),
eq("ssn", "21785999") ) );
List <Map<String, Object>> list =
repo.query(selects(select("firstName")), eq("lastName", "Hightower"));
List <Map<String, Object>> list =
repo.sortedQuery("firstName",selects(select("firstName")),
eq("lastName", "Hightower"));
List <Map<String, Object>> list = repo.query(
selects(select("department", "name")),
eq("lastName", "Hightower"));
assertEquals("engineering", list.get(0).get("department.name"));
List <Map<String, Object>> list = repo.query(
selects(selectPropPath("department", "name")),
eq("lastName", "Hightower"));
repoBuilder.primaryKey("ssn")
.searchIndex("firstName").searchIndex("lastName")
.searchIndex("salary").searchIndex("empNum", true)
.usePropertyForAccess(true);
List<Map<String, Object>> employees = repo.queryAsMaps(eq("firstName", "Diana"));
System.out.println(employees.get(0).get("department"));
{class=Department, name=engineering}
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));
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]},
...
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 {
...
List<Employee> results = sortedQuery(queryableList, "firstName", typeOf("SalesEmployee"));
assertEquals(1, results.size());
assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
List<Employee> results = sortedQuery(queryableList, "firstName", typeOf("SalesEmployee"));
assertEquals(1, results.size());
assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
List<Employee> results = sortedQuery(queryableList, "firstName", instanceOf(SalesEmployee.class));
assertEquals(1, results.size());
assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
List<Employee> results = sortedQuery(queryableList, "firstName",
implementsInterface(Comparable.class));
assertEquals(1, results.size());
assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
/* 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");
List<Map<String, Object>> list = repo.query(
selects(select("tags", "metas", "metas2", "name2")),
eqNested("2tag1", "tags", "metas", "metas2", "name2"));
List<Map<String, Object>> list = repo.query(
selects(select("tags", "metas", "metas2", "name2")),
eq("tags.metas.metas2.name2", "2tag1"));
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());
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"));
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
/**
* 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> {
}
}
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");