예, HashMap
그러나 ...하지만 특수한 방법으로 사용하십시오 HashMap
. 의사 로 사용하려고 할 때 예상되는 함정 Set
은의 "실제"요소 Map/Set
와 "후보"요소, 즉 equal
요소가 이미 존재합니다. 이것은 절대로 멀지 않지만 함정에서 멀어지게합니다.
class SelfMappingHashMap<V> extends HashMap<V, V>{
@Override
public String toString(){
// otherwise you get lots of "... object1=object1, object2=object2..." stuff
return keySet().toString();
}
@Override
public V get( Object key ){
throw new UnsupportedOperationException( "use tryToGetRealFromCandidate()");
}
@Override
public V put( V key, V value ){
// thorny issue here: if you were indavertently to `put`
// a "candidate instance" with the element already in the `Map/Set`:
// these will obviously be considered equivalent
assert key.equals( value );
return super.put( key, value );
}
public V tryToGetRealFromCandidate( V key ){
return super.get(key);
}
}
그런 다음이 작업을 수행하십시오.
SelfMappingHashMap<SomeClass> selfMap = new SelfMappingHashMap<SomeClass>();
...
SomeClass candidate = new SomeClass();
if( selfMap.contains( candidate ) ){
SomeClass realThing = selfMap.tryToGetRealFromCandidate( candidate );
...
realThing.useInSomeWay()...
}
그러나 ... 이제 candidate
프로그래머가 실제로 즉시 넣지 않는 한 어떤 식 으로든 자폭 Map/Set
하기 contains
를 원합니다 ... 당신 은 그것이 "분석"에 candidate
합류하지 않으면 그것을 사용하도록 "오염" 하고 싶습니다. Map
". 아마도 SomeClass
새로운 Taintable
인터페이스를 구현할 수있을 것 입니다.
보다 만족스러운 솔루션은 아래와 같이 GettableSet 입니다. 그러나 이것이 작동하려면 SomeClass
모든 생성자를 볼 수 없게 만들거나 래퍼 클래스를 디자인하고 사용할 수있는 디자인을 담당해야합니다.
public interface NoVisibleConstructor {
// again, this is a "nudge" technique, in the sense that there is no known method of
// making an interface enforce "no visible constructor" in its implementing classes
// - of course when Java finally implements full multiple inheritance some reflection
// technique might be used...
NoVisibleConstructor addOrGetExisting( GettableSet<? extends NoVisibleConstructor> gettableSet );
};
public interface GettableSet<V extends NoVisibleConstructor> extends Set<V> {
V getGenuineFromImpostor( V impostor ); // see below for naming
}
이행:
public class GettableHashSet<V extends NoVisibleConstructor> implements GettableSet<V> {
private Map<V, V> map = new HashMap<V, V>();
@Override
public V getGenuineFromImpostor(V impostor ) {
return map.get( impostor );
}
@Override
public int size() {
return map.size();
}
@Override
public boolean contains(Object o) {
return map.containsKey( o );
}
@Override
public boolean add(V e) {
assert e != null;
V result = map.put( e, e );
return result != null;
}
@Override
public boolean remove(Object o) {
V result = map.remove( o );
return result != null;
}
@Override
public boolean addAll(Collection<? extends V> c) {
// for example:
throw new UnsupportedOperationException();
}
@Override
public void clear() {
map.clear();
}
// implement the other methods from Set ...
}
귀하의 NoVisibleConstructor
클래스는 다음과 같이 :
class SomeClass implements NoVisibleConstructor {
private SomeClass( Object param1, Object param2 ){
// ...
}
static SomeClass getOrCreate( GettableSet<SomeClass> gettableSet, Object param1, Object param2 ) {
SomeClass candidate = new SomeClass( param1, param2 );
if (gettableSet.contains(candidate)) {
// obviously this then means that the candidate "fails" (or is revealed
// to be an "impostor" if you will). Return the existing element:
return gettableSet.getGenuineFromImpostor(candidate);
}
gettableSet.add( candidate );
return candidate;
}
@Override
public NoVisibleConstructor addOrGetExisting( GettableSet<? extends NoVisibleConstructor> gettableSet ){
// more elegant implementation-hiding: see below
}
}
PS 그러한 NoVisibleConstructor
클래스의 기술적 문제 : 그러한 클래스는 본질적 final
으로 바람직하지 않을 수 있습니다. 실제로 항상 더미 매개 변수가없는 protected
생성자를 추가 할 수 있습니다 .
protected SomeClass(){
throw new UnsupportedOperationException();
}
... 적어도 서브 클래스를 컴파일 할 수 있습니다. 그런 다음 getOrCreate()
서브 클래스에 다른 팩토리 메소드 를 포함해야하는지 여부를 고려해야합니다.
마지막 단계 는 세트 멤버에 대한 추상 기본 클래스 (목록의 경우 "NB"요소, 세트의 "멤버")입니다 (가능한 경우 클래스가 제어 할 수없는 랩퍼 클래스 사용 범위 , 최대 구현 숨기기를 위해 이미 기본 클래스 등이 있습니다.
public abstract class AbstractSetMember implements NoVisibleConstructor {
@Override
public NoVisibleConstructor
addOrGetExisting(GettableSet<? extends NoVisibleConstructor> gettableSet) {
AbstractSetMember member = this;
@SuppressWarnings("unchecked") // unavoidable!
GettableSet<AbstractSetMembers> set = (GettableSet<AbstractSetMember>) gettableSet;
if (gettableSet.contains( member )) {
member = set.getGenuineFromImpostor( member );
cleanUpAfterFindingGenuine( set );
} else {
addNewToSet( set );
}
return member;
}
abstract public void addNewToSet(GettableSet<? extends AbstractSetMember> gettableSet );
abstract public void cleanUpAfterFindingGenuine(GettableSet<? extends AbstractSetMember> gettableSet );
}
... 사용법은 (당신의 내부에 매우 분명하다 SomeClass
의 static
팩토리 메소드) :
SomeClass setMember = new SomeClass( param1, param2 ).addOrGetExisting( set );
SortedSet
맵 기반의 구현 및 구현을 사용하십시오 (예 :TreeSet
액세스 가능first()
).