Java集合的多个索引-最基本的解决方案?

Java集合的多个索引-最基本的解决方案?,java,collections,indexing,Java,Collections,Indexing,我正在寻找在Java集合上创建多个索引的最基本解决方案 所需功能: 删除某个值时,必须删除与该值关联的所有索引项 索引查找必须比线性搜索快(至少与树映射一样快) 附带条件: 不依赖于大型(如Lucene)库。没有不常见或未经良好测试的库。没有数据库 像ApacheCommons Collections之类的库就可以了 如果它能单独与JavaSE(6.0)一起工作,那就更好了 编辑:没有自我实现的解决方案(感谢您的回答,这是一个很好的建议,但是我已经有了一个与Jay非常相似的解决方案),每当

我正在寻找在Java集合上创建多个索引的最基本解决方案

所需功能:


  • 删除某个值时,必须删除与该值关联的所有索引项
  • 索引查找必须比线性搜索快(至少与树映射一样快)
附带条件:

  • 不依赖于大型(如Lucene)库。没有不常见或未经良好测试的库。没有数据库
  • 像ApacheCommons Collections之类的库就可以了
  • 如果它能单独与JavaSE(6.0)一起工作,那就更好了
  • 编辑:没有自我实现的解决方案(感谢您的回答,这是一个很好的建议,但是我已经有了一个与Jay非常相似的解决方案),每当有人发现他们实现了相同的东西时,这应该是一些公共库的一部分
当然,我可以自己编写一个管理多个贴图的类(这并不难,但感觉就像是重新发明了轮子)。所以我想知道,如果不使用,它是否可以完成,同时仍然可以获得类似于使用单个索引java.util.Map的简单用法

谢谢,克里斯

更新 看来我们什么也没找到。我喜欢你所有的答案——自我开发的版本,链接到数据库类库

以下是我真正想要的:在(a)ApacheCommons集合或(b)Google Collections/Guava中拥有这些功能。或者是一个很好的选择


其他人也会错过这些库中的这个功能吗?他们确实提供了各种各样的东西,比如多重映射、多重键映射、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");