Collections.newSetFromMap源码解析及SetFromMap的使用场景

在Spring源码中,我们能看到很多如下代码:

Collections.newSetFromMap(new ConcurrentHashMap<>(16))

代码的作用就是获取一个Set对象,那么为什么不直接new一个HashSet而要从Map中获取呢?

跟踪源码我们可以看到通过newSetFromMap获取到的Set实现类为:

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

    // Override default methods in Collection
    @Override
    public void forEach(Consumer<? super E> action) {
        s.forEach(action);
    }
    @Override
    public boolean removeIf(Predicate<? super E> filter) {
        return s.removeIf(filter);
    }

    @Override
    public Spliterator<E> spliterator() {return s.spliterator();}
    @Override
    public Stream<E> stream()           {return s.stream();}
    @Override
    public Stream<E> parallelStream()   {return s.parallelStream();}

    private static final long serialVersionUID = 2454657854757543876L;

    private void readObject(java.io.ObjectInputStream stream)
        throws IOException, ClassNotFoundException
    {
        stream.defaultReadObject();
        s = m.keySet();
    }
}

SetFromMap有2个属性,一个是用来存储真实数据的map,一个是map的keySet()。

从数据结构上分析,Map是一个key-value结构,而Set我们可以认为就是一个简化的只有key的Map,事实上我们可以把一个Map当成一个Set来用,所以有了SetFromMap这个Set的实现类。通过其源码我们也可以看到,所有的操作都是通过map代理实现的。

我们知道ConcurrentHashMap是Java 5中支持高并发、高吞吐量的线程安全Map实现。而在java中,并没有一个对应的ConcurrentHashSet的Set实现。为了实现ConcurrentHashSet,我们可以使用SetFromMap进行代理实现。

所以SetFromMap的使用场景就在于,当我们拥有一个不同的XMap实现时,想要一个类似功能的XSet实现,我们不需要重新去新增一个XSet,可以直接通过SetFromMap代理一个XMap来快速达到拥有XSet的操作。


觉得内容还不错?打赏个钢镚鼓励鼓励!!👍