2009-07-07 8 views
5

問題:Javaオブジェクト間で双方向の多対1の関係を維持します。Javaの単純なデータベースのようなコレクションクラス

グーグル/コモンズコレクション双方向マップのようなものに、私はにしたいが、前方側の重複する値を許可し、セット裏側値として前進キーのを持っています。このような 使用される何か:

// 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 */ } 

単純なJavaのアプローチがあります。1.いずれかMap<Location, Region>またはMap<Region, Set<Location>>として、関係の片側のみを維持し、必要なときに反復によって逆の関係を収集するために、または、2.両側のマップを維持するラッパーを作成し、両側を同期させるためにすべての変更呼び出しを傍受します。

1はO(log n)ではなくO(n)であり、問​​題になっています。私は2でスタートし、すぐに雑草に入った。

これはSQL世界ではほとんどありません(Locationテーブルはインデックス付きのRegionIDカラムを取得します)。通常のオブジェクトにはそれほど些細なことがあります。

+1

私は実際に何をしているのかわからないので、これを答えにするのは嫌ですが、その価値については間違ったデータ構造を使用していると思います。あなたが描いていることは、マップやセットではなく、すべての頂点が他の頂点の任意の数に隣接しているグラフです。あなたは、適切なグラフデータ構造を使用することによって、より良いプログラミングモデル+ランタイムを得るでしょう。おそらく – Juliet

+0

。私がMaps and Setsから得たのは、基本的なセマンティクスとlog-nパフォーマンスです。より良い構造が、物事の漠然としたマップ - アイ "ビュー"を提供すれば、完璧です。 http://jung.sourceforge.net/を検索中です。今すぐ削除された回答のおかげで有望そうです。 – rgeorge

+0

挿入コストは重要ですか? (また、その最後の行のotherset意味ですか?) – Stobor

答えて

0

私はあなたが次の2つのクラスで必要なものを達成できると思います。 2つのマップが含まれていますが、外界に露出されていないため、同期が外れる方法はありません。同じ「事実」を2回保管するのは、事実がここにあるように2回明示的に保管されるか、またはデータベースが索引を作成するときに暗黙的に保管されるかに関わらず、 2つのテーブルでジョインをより効率的にすることができます。あなたは新しいものをmagicsetに追加することができ、両方のマッピングを更新するか、マジックマッパーに物を追加することができます。そしてマジックマッパーは逆マップを自動で更新します。ガールフレンドは私をベッドに呼んでいるので、これをコンパイラで実行することはできません。どんなパズルを解決しようとしていますか?

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 
} 


} 
+0

うわー。ありがとう。それは確かに失敗した試行#2のラインに沿っています。私はおそらくより小さな咬み込みを試みる必要があります。 調査中のパズルはぬりかえです - 私は自分の使い方でヌリカカビのパズルを作るためのエディターを作っています(puzzle-nurikabe.comの自動生成されたパズルはmehです)。このために私は速いソルバーが必要です。難易度を推定する。 – rgeorge

+0

なんらかの理由でjava.util SetとMapのインタフェースを実装したかったのであれば、AbstractMapとAbstractSetのjavadocを読んでください。 –

1

私はあなたのモデルを誤解するかもしれませんが、あなたの場所と地域が正しいequals()とhashCode()を実装しているならば、Location - > Regionのセットは単なる古典的な単純なMap実装です同じオブジェクト値)。リージョン - >位置のセットはマルチマップです(Google Collで利用可能)。両方のサブマップを操作するために、適切な追加/削除メソッドを使用して独自のクラスを作成できます。

おそらく、過剰なものですが、インメモリSQLサーバ(HSQLDBなど)を使用することもできます。これにより、多くの列に索引を作成できます。

+0

ええ、2つのサブマップから構成することは、私が避けようとしていることです。同じ「事実」を2回(「AはB」に、「BはAを含む」)保存し、2つの一貫性を保つためにキャッチしなければならない地図エントリを変更する方法が*たくさんあります。私は、私の新しいデータ構造を使ってより良い方法を実行しなければ、sqliteなどを使ってSQLのことをやってしまうかもしれません。 – rgeorge

+2

もし私が私だったら、 'Set'と' Map'sが内部的に混乱していても、外部インターフェースに対していくつかのテストを書いて、後で内部的に置き換えてしまうより良いデータ構造。 (そして、私が必要とするよりも一般的な外部インターフェイスを作ってはいけません) サブマップをカプセル化して、インターフェイスの外部からアクセスできないようにすることはできません。次に、あなたは簿記のためのサブマップを使用しているだけで、実際に何かを "キャッチ"する必要はありません。 –

関連する問題