Có, sử dụng HashMap
... nhưng theo một cách chuyên biệt: cái bẫy mà tôi thấy trước khi cố gắng sử dụng HashMap
như một giả - Set
là sự nhầm lẫn có thể có giữa các yếu tố "thực tế" của các yếu tố Map/Set
và "ứng cử viên", tức là các yếu tố được sử dụng để kiểm tra xem equal
yếu tố đã có mặt. Điều này là xa vời, nhưng đẩy bạn ra khỏi cái bẫy:
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);
}
}
Sau đó, làm điều này:
SelfMappingHashMap<SomeClass> selfMap = new SelfMappingHashMap<SomeClass>();
...
SomeClass candidate = new SomeClass();
if( selfMap.contains( candidate ) ){
SomeClass realThing = selfMap.tryToGetRealFromCandidate( candidate );
...
realThing.useInSomeWay()...
}
Nhưng ... bây giờ bạn muốn candidate
tự hủy theo một cách nào đó trừ khi lập trình viên thực sự ngay lập tức đưa nó vào Map/Set
... bạn muốn contains
"làm mờ" candidate
để bất kỳ việc sử dụng nào trừ khi nó tham gia vào Map
"anathema" ". Có lẽ bạn có thể thực SomeClass
hiện một Taintable
giao diện mới .
Một giải pháp thỏa đáng hơn là một Gfortset , như dưới đây. Tuy nhiên, để làm việc này, bạn phải chịu trách nhiệm thiết kế SomeClass
để làm cho tất cả các nhà xây dựng không nhìn thấy được (hoặc ... có thể và sẵn sàng thiết kế và sử dụng lớp trình bao bọc cho nó):
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
}
Thực hiện:
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 ...
}
Các NoVisibleConstructor
lớp học của bạn sau đó trông như thế này:
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 một vấn đề kỹ thuật với một NoVisibleConstructor
lớp như vậy : có thể bị phản đối rằng một lớp như vậy vốn có final
, điều này có thể là không mong muốn. Trên thực tế, bạn luôn có thể thêm một hàm tạo không tham số giả protected
:
protected SomeClass(){
throw new UnsupportedOperationException();
}
... mà ít nhất sẽ để một lớp con biên dịch. Sau đó, bạn phải suy nghĩ về việc bạn có cần đưa getOrCreate()
phương thức xuất xưởng khác vào lớp con hay không.
Bước cuối cùng là một lớp cơ sở trừu tượng (NB "phần tử" cho một danh sách, "thành viên" cho một tập hợp) như thế này cho các thành viên tập hợp của bạn (khi có thể - một lần nữa, phạm vi sử dụng lớp trình bao trong đó lớp không nằm trong tầm kiểm soát của bạn, hoặc đã có một lớp cơ sở, v.v.), để ẩn thực hiện tối đa:
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 );
}
... sử dụng là khá rõ ràng (bên trong của bạn SomeClass
's static
phương pháp nhà máy):
SomeClass setMember = new SomeClass( param1, param2 ).addOrGetExisting( set );
SortedSet
và triển khai nó, dựa trên bản đồ (ví dụ:TreeSet
cho phép truy cậpfirst()
).