Java中类似数据库的简单集合类

Java中类似数据库的简单集合类,java,database,algorithm,data-structures,collections,Java,Database,Algorithm,Data Structures,Collections,问题是:在java对象之间保持双向多对一关系 类似于Google/Commons Collections bidi maps,但我希望允许在正向上重复值,并将一组正向键作为反向值。 使用类似这样的方式: // maintaining disjoint areas on a gameboard. Location is a space on the // gameboard; Regions refer to disjoint collections of Locations. MagicalM

问题是:在java对象之间保持双向多对一关系

类似于Google/Commons Collections bidi maps,但我希望允许在正向上重复值,并将一组正向键作为反向值。 使用类似这样的方式:

// maintaining disjoint areas on a gameboard. Location is a space on the
// gameboard; Regions refer to disjoint collections of Locations.

MagicalManyToOneMap<Location, Region> forward = // the game universe
Map<Region, <Set<Location>>> inverse = forward.getInverse(); // live, not a copy
Location parkplace = Game.chooseSomeLocation(...);
Region mine = forward.get(parkplace); // assume !null; should be O(log n)
Region other = Game.getSomeOtherRegion(...);
// moving a Location from one Region to another:
forward.put(parkplace, other);
// or equivalently:
inverse.get(other).add(parkplace); // should also be O(log n) or so
// expected consistency:
assert ! inverse.get(mine).contains(parkplace);
assert forward.get(parkplace) == other;
// and this should be fast, not iterate every possible location just to filter for mine:
for (Location l : mine) { /* do something clever */ }
//在游戏板上维护不相交的区域。位置是一个空间上的地图
//游戏板;区域是指不相交的位置集合。
MagicalManyToOneMap forward=//游戏世界
Map inverse=forward.getInverse();//活的,不是复制的
地点parkplace=游戏。选择某个地点(…);
区域地雷=前进。获取(停车场);//假定无效的应为O(日志n)
Region other=Game.getSomeOtherRegion(…);
//将位置从一个区域移动到另一个区域:
提出(公园、其他);
//或相当于:
反向。获取(其他)。添加(停车场);//也应该是O(logn)左右
//预期一致性:
断言!反向。获取(我的)。包含(公园);
断言向前。get(parkplace)=其他;
//这应该很快,而不是迭代每个可能的位置来筛选我的位置:
对于(位置l:我的){/*做点聪明的事*/}
简单的java方法有:1。仅维护关系的一侧,作为
映射
映射
,并在需要时通过迭代收集反向关系;或者,2。制作一个包装器来维护双方的映射,并截获所有变异调用以保持双方同步

1是O(n)而不是O(logn),这正成为一个问题。我从2号开始,马上就到了杂草丛中。(知道有多少种不同的方法可以更改地图条目?)


这在sql世界中几乎是微不足道的(Location表获得一个索引RegionID列)。有没有什么明显的地方让我忽略了,这使得它对于普通对象来说是微不足道的?

我可能误解了您的模型,但是如果您的位置和区域实现了正确的equals()和hashCode(),那么位置->区域集只是一个经典的简单映射实现(多个不同的键可以指向相同的对象值)。区域->位置集是一个多地图(在Google Coll.中提供)。您可以使用适当的add/remove方法组合自己的类来操作这两个子映射


可能有点过分,但也可以使用内存中的sql server(HSQLDB等)。它允许您在许多列上创建索引。

我认为您可以通过以下两个类实现所需的功能。虽然它确实涉及两个地图,但它们不会暴露于外部世界,因此不应该有一种方法让它们失去同步。至于将同一个“事实”存储两次,我认为在任何有效的实现中,您都无法回避这一点,无论事实是显式存储两次,还是隐式存储两次,就像您的数据库创建索引以使联接在两个表上更有效一样。您可以向magicset添加新内容,它将更新两个映射,或者您可以向magicmapper添加内容,magicmapper随后将自动更新反向映射。女朋友现在叫我上床睡觉,所以我不能通过编译器运行这个程序——这应该足够让你开始了。你想解决什么难题

public class MagicSet<L> {
   private Map<L,R> forward;
   private R r;
   private Set<L> set;

   public MagicSet<L>(Map forward, R r) {
       this.forward = map;
       this.r = r;
       this.set = new HashSet<L>();
   }

   public void add(L l) {
       set.add(l);
       forward.put(l,r);
   }

   public void remove(L l) {
       set.remove(l);
       forward.remove(l);
   }

   public int size() {
       return set.size();
   }

   public in contains(L l){
       return set.contains(l);
   }

   // caution, do not use the remove method from this iterator. if this class was going
   // to be reused often you would want to return a wrapped iterator that handled the remove method properly.  In fact, if you did that, i think you could then extend AbstractSet and MagicSet would then fully implement java.util.Set.
   public Iterator iterator() {
       return set.iterator();  
   }
}



public class MagicMapper<L,R> { // note that it doesn't implement Map, though it could with some extra work.  I don't get the impression you need that though.
 private Map<L,R> forward;
 private Map<R,MagicSet<L>> inverse;

 public MagicMapper<L,R>() {
       forward = new HashMap<L,R>;
       inverse = new HashMap<R,<MagicSet<L>>;
 }


 public R getForward(L key) {
       return forward.get(key);
 }


 public Set<L> getBackward(R key) {
       return inverse.get(key);  // this assumes you want a null if
                               // you try to use a key that has no mapping.  otherwise you'd return a blank MagicSet
 }

 public void put (L l, R r) {

       R oldVal = forward.get(l);

       // if the L had already belonged to an R, we need to undo that mapping
       MagicSet<L> oldSet = inverse.get(oldVal);

       if (oldSet != null) {oldSet.remove(l);}

       // now get the set the R belongs to, and add it.
       MagicSet<L> newSet = inverse.get(l);

       if (newSet == null) {
               newSet = new MagicSet<L>(forward, r);
               inverse.put(r,newSet);
       }
       newSet.add(l);  // magically updates the "forward" map
 }


}
公共类MagicSet{
私人地图转发;
私人住宅;
私有集;
公共MagicSet(向前映射,右){
this.forward=map;
这个。r=r;
this.set=新的HashSet();
}
公共无效添加(L){
增加(1);
提出(l,r);
}
公共空间移除(L){
设置。移除(l);
向前。移除(l);
}
公共整数大小(){
返回set.size();
}
公共包含(L){
返回集。包含(l);
}
//注意,不要在此迭代器中使用remove方法
//要经常重用,您需要返回一个正确处理remove方法的包装迭代器。事实上,如果这样做,我认为您可以扩展AbstractSet,MagicSet将完全实现java.util.Set。
公共迭代器迭代器(){
返回set.iterator();
}
}
公共类MagicMapper{//注意,它没有实现Map,尽管它需要一些额外的工作。但是我没有得到您需要的印象。
私人地图转发;
私有映射逆;
公共MagicMapper(){
前进=新的HashMap;

inverse=new HashMapI我不愿意把它作为答案,因为我不知道你实际上在做什么,但为了它的价值,我认为你只是使用了错误的数据结构。你所描述的不是一个地图或集合,而是一个所有顶点都与任何数量的其他顶点相邻的图形。你会得到一个更好的编程模型+运行时使用适当的图形数据结构,而不是随意地将集合和映射粘合在一起。可能。我所追求的是从映射和集合中获得的非常基本的语义和log-n性能。如果更好的结构提供模糊的映射“视图”当然,很好。由于现在删除了一个答案,我正在探索,它看起来很有希望。插入的成本对你来说重要吗?(还有,你是说最后一行中的其他集?)是的。将位置划分为不相交的区域是回溯谜题解算器的一个尝试解决方案,因此可能有O(2^n)个位置(我将其大幅缩减。)不过,删除并不是问题。我也对代码做了一些澄清——是的,我认为一个位置应该直接映射到它的区域(实际上只是一个标记),并将区域映射回其位置集合。在该方向上,我需要的唯一设置质量是快速迭代;正向映射已经是一个快速的成员资格测试。是的,由两个子映射组成是我试图避免的。它两次存储相同的“事实”(“a在B中”和“B包含a”),有很多方法可以修改映射条目,我必须抓住这些方法来保持两者的一致性