Collections提供了一种保证元素唯一性的Map实现,就是用一个Set来表示Map,它持有这个Map的引用,并且保持Map的顺序、并发和性能特征。这就是newSetFromMap方法:
public static <E> Set<E> newSetFromMap(Map<E, Boolean> map) {
return new SetFromMap<>(map);
}
从Collections中可以看到,这里使用了一个内部类SetFromMap,它实现了Set接口。使用方法就是将Map的实例作为构造参数传入构造方法,并且这个Map必须是空的,否则抛出非法参数的异常:
SetFromMap(Map<E, Boolean> map) {
if (!map.isEmpty())
throw new IllegalArgumentException("Map is non-empty");
m = map;
s = map.keySet();
}
一般的最佳实践是,用一个空的Map传入构造方法,比如:
Set<Object> weakHashSet = Collections.newSetFromMap(new WeakHashMap<Object, Boolean>());
Set中的值就用Map的key来存储。用Map的put方法来add,就是这样:
public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; }
这里的m就是持有的Map。又因为addAll的实现是多次调用add方法,因为在这里就是多次调用m.put,而addAll在Collections中已经实现了。因此addAll是唯一一个继承的方法。具体的内部类定义见下:
private static class SetFromMap<E> extends AbstractSet<E>
implements Set<E>, Serializable
{
private final Map<E, Boolean> m; // The backing map
private transient Set<E> s; // Its keySet
SetFromMap(Map<E, Boolean> map) {
if (!map.isEmpty())
throw new IllegalArgumentException("Map is non-empty");
m = map;
s = map.keySet();
}
public void clear() { m.clear(); }
public int size() { return m.size(); }
public boolean isEmpty() { return m.isEmpty(); }
public boolean contains(Object o) { return m.containsKey(o); }
public boolean remove(Object o) { return m.remove(o) != null; }
public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; }
public Iterator<E> iterator() { return s.iterator(); }
public Object[] toArray() { return s.toArray(); }
public <T> T[] toArray(T[] a) { return s.toArray(a); }
public String toString() { return s.toString(); }
public int hashCode() { return s.hashCode(); }
public boolean equals(Object o) { return o == this || s.equals(o); }
public boolean containsAll(Collection<?> c) {return s.containsAll(c);}
public boolean removeAll(Collection<?> c) {return s.removeAll(c);}
public boolean retainAll(Collection<?> c) {return s.retainAll(c);}
// addAll is the only inherited implementation
private static final long serialVersionUID = 2454657854757543876L;
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException
{
stream.defaultReadObject();
s = m.keySet();
}
}
--------------------------------------------应用实例------------------------------------------------
在spring framework中AbstractBeanFactory中使用了一个名为alreadyCreated的Set来存放已经定义过得bean的名字。它的定义如下:
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(64));
因为java中没有ConcurrentSet这种东西,而从逻辑上讲又应当使用一个Set来实现,因此这里就使用了上面所说的方法。它是一个SetFromMap的实例。这样,既保持了alreadyCreated作为一个Set变量的逻辑意义,又通过传入的ConcurrentHashMap保证了它的并发性。