हां, का उपयोग करें 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
इंटरफ़ेस लागू कर सकते हैं ।
नीचे के रूप में एक अधिक संतोषजनक समाधान गेटटेबसेट है । हालाँकि, इसके लिए आपको काम करने के लिए या तो 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
}
}
ऐसी NoVisibleConstructor
कक्षा के साथ PS एक तकनीकी समस्या : यह आपत्ति हो सकती है कि ऐसा वर्ग स्वाभाविक है 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()
)।