Ya, gunakan HashMap
... tetapi dengan cara khusus: jebakan yang saya ramalkan dalam mencoba menggunakan a HashMap
sebagai pseudo- Set
adalah kemungkinan kebingungan antara elemen "aktual" elemen Map/Set
, dan "kandidat", yaitu elemen yang digunakan untuk menguji apakah suatu equal
elemen sudah ada. Ini jauh dari sangat mudah, tetapi mendorong Anda menjauh dari perangkap:
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);
}
}
Kemudian lakukan ini:
SelfMappingHashMap<SomeClass> selfMap = new SelfMappingHashMap<SomeClass>();
...
SomeClass candidate = new SomeClass();
if( selfMap.contains( candidate ) ){
SomeClass realThing = selfMap.tryToGetRealFromCandidate( candidate );
...
realThing.useInSomeWay()...
}
Tapi ... Anda sekarang ingin candidate
merusak diri sendiri dalam beberapa cara kecuali programmer benar-benar segera meletakkannya di Map/Set
... Anda ingin contains
"mencemari" candidate
sehingga setiap penggunaannya kecuali jika bergabung dengan Map
membuatnya menjadi "kutukan" ". Mungkin Anda bisa membuat SomeClass
implement yang baruTaintable
antarmuka .
Solusi yang lebih memuaskan adalah GettableSet , seperti di bawah ini. Namun, agar ini berfungsi, Anda harus bertanggung jawab atas desain SomeClass
untuk membuat semua konstruktor tidak terlihat (atau ... mampu dan mau merancang dan menggunakan kelas wrapper untuk itu):
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
}
Penerapan:
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
Kelas Anda kemudian terlihat seperti ini:
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 satu masalah teknis dengan NoVisibleConstructor
kelas seperti itu: mungkin keberatan bahwa kelas seperti itu secara inheren final
, yang mungkin tidak diinginkan. Sebenarnya Anda selalu bisa menambahkan protected
konstruktor tanpa parameter dummy :
protected SomeClass(){
throw new UnsupportedOperationException();
}
... yang paling tidak akan membiarkan kompilasi subclass. Anda kemudian harus memikirkan apakah Anda perlu memasukkan getOrCreate()
metode pabrik lain dalam subkelas.
Langkah terakhir adalah kelas dasar abstrak (NB "elemen" untuk daftar, "anggota" untuk set) seperti ini untuk anggota set Anda (bila mungkin - lagi, ruang lingkup untuk menggunakan kelas wrapper di mana kelas tidak di bawah kendali Anda, atau sudah memiliki kelas dasar, dll.), untuk menyembunyikan implementasi maksimal:
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 );
}
... penggunaan cukup jelas (dalam Anda SomeClass
's static
metode pabrik):
SomeClass setMember = new SomeClass( param1, param2 ).addOrGetExisting( set );
SortedSet
dan implementasinya, yang berbasis peta (mis.TreeSet
Memungkinkan untuk mengaksesfirst()
).