是的,使用HashMap
...,但是以一种特殊的方式:我预见到试图将a 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()
)。