2009-07-07 5 views
5

문제점 : Java 객체간에 양방향 다 대일 관계를 유지하십시오. 구글/커먼즈 컬렉션 쌍방향지도와 같은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 */ } 

1. 관계의 한 측면을 유지하기 위해 어느 필요할 때 Map<Location, Region> 또는 Map<Region, Set<Location>>, 그리고 반복하여 역관계를 수집로서 또는 2. 양측지도를 유지하는 래퍼를 만들고 양측을 동기화 상태로 유지하기 위해 모든 변경 전화를 차단합니다.

1은 O (log n) 대신 O (n)이되어 문제가되고 있습니다. 나는 2시에 시작했고 곧 잡초에있었습니다. (Map 엔트리를 변경하는 방법이 얼마나 많이 있는지 알 수 있습니다.)

이것은 SQL 세계에서 거의 사소한 것입니다 (Location 테이블은 인덱싱 된 RegionID 열을 가져옵니다). 일반 오브젝트에 대해 사소한 것이 분명해졌습니다.

+1

은 내가 당신이 실제로 무슨 일을하는지 알 수 없기 때문에 대답으로이를 넣어하기를 꺼려 해요,하지만 그 가치를 위해, 당신은 단지 잘못된 데이터 구조를 사용하고 생각합니다. 당신이 설명하는 것은지도 또는 집합이 아니며, 모든 정점이 다른 정점에 인접한 그래프입니다. 당신은 적절한 그래프 데이터 구조를 사용하여 더 나은 프로그래밍 모델 + 런타임을 얻게 될 것입니다. 아마도 – Juliet

+0

일 것입니다. Maps and Sets의 모든 기능은 매우 기본적인 의미와 log-n 성능입니다. 더 나은 구조가 사물의 막연한 "보기"를 제공한다면, 완벽합니다. 지금 삭제 된 답변 덕택에 http://jung.sourceforge.net/을 (를) 탐색 중이며 유망 해 보입니다. – rgeorge

+0

삽입 비용이 중요합니까? (또한, 그 마지막 줄에서 otherset 의미합니까?) – Stobor

답변

0

다음과 같은 두 가지 클래스를 통해 필요한 것을 얻을 수 있다고 생각합니다. 두 개의지도가 포함되어 있지만 외부 세계에 노출되지 않으므로 동기화되지 못하게하는 방법이 없어야합니다. 동일한 "사실"을 두 번 저장하는 것에 관해서는 사실이 여기에 명시된대로 명시 적으로 두 번 저장되는지 또는 데이터베이스가 인덱스를 만들 때 그대로 암시 적으로 저장되는지에 상관없이 효율적인 구현에서이를 해결할 것이라고는 생각하지 않습니다. 2 개의 테이블에서 더 효율적으로 조인을 만들 수 있습니다. magicset에 새로운 것을 추가하면 두 매핑을 모두 업데이트하거나 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 
} 


} 
+0

와우. 감사. 그건 실제로 내 실패한 시도 # 2의 라인을 따라있다. 나는 아마 작은 물기를 가지고 가려고 노력해야한다. 조사중인 퍼즐은 누리 카베 (Nurikabe)입니다. 저는 누리 카베 (Nurikabe.com)의 자동 생성 된 퍼즐을 만들기 위해 내 자신을 위해 편집자를 만들고 있습니다. 퍼즐 용 퍼즐은 meh입니다. 어려움을 예측합니다. – rgeorge

+0

어떤 이유에서든 java.util Set 및 Map 인터페이스를 구현하고 싶다면 AbstractMap과 AbstractSet에서 javadoc을 읽어야합니다. –

1

모델을 오해 할 수도 있지만 위치 및 지역이 올바른 equals() 및 hashCode()를 구현 한 경우 위치 -> 지역 세트는 단순한 고전적인 간단한지도 구현입니다 (여러 개의 고유 키가 동일한 객체 값). Region -> Set of Location은 Multimap입니다 (Google Coll에서 사용 가능). 두 개의 서브맵을 모두 조작하기 위해 적절한 추가/제거 메소드로 클래스를 작성할 수 있습니다.

아마도 과잉이지만, 메모리 내장 SQL 서버 (HSQLDB 등)를 사용할 수도 있습니다. 그것은 당신이 많은 열에 색인을 만들 수 있습니다.

+0

그래, 두 개의 서브 맵을 구성하는 것은 내가 피하려고하는 것이다. 동일한 "사실"을 두 번 저장합니다 ("A는 B"이고 "B는 A"를 포함합니다). 두 가지를 일관되게 유지하기 위해 잡아야 할지도 항목을 수정하는 방법이 많이 있습니다. 나는 나에게 새로운 데이터 구조를 사용하는 더 좋은 방법을 가로 질러 실행하지 않는다면, sqlite 등으로 sql 일을하는 것이 좋을 수도있다. – rgeorge

+2

그것이 나 였다면, 나는'Set'과'Map's의 내부적 인 혼란이 무엇이든 상관없이 함께 생각해 보았습니다. 외부 인터페이스에 대한 몇 가지 테스트를 작성하고 나중에 실행 한 경우 나중에 내부를 교체하는 방법을 그림으로 작성했습니다. 더 나은 데이터 구조. (외부 인터페이스를 필요 이상으로 일반화하지는 않습니다.) 서브맵을 캡슐화하여 인터페이스 외부에서 액세스 할 수 없으며 실제 맵 항목을 노출시키지 못합니까? 그럼 당신은 단지 부기를위한 서브맵을 사용하고 있고, 당신은 정말로 아무것도 잡을 필요가 없습니다. –

관련 문제