Set集合

在数学上集合是一个无序、无重复和任意个具有相关特性的离散数据的组织。在数据结构中,集合是一个无序、无重复和任意个具有相关特性的对象的组织。先纵观下Set集合的框架图,如下图所示。
Set集合_第1张图片

Set、AbstractSet

JDK中Collection都有适配器,而这里要说的是Set接口源码,细心的朋友会发现Set内的Method和Collection的Method完全相同,而它的适配器AbstractSet也只是实现了equals、hascode和removeAll三个基本方法。Set既然继承了Collection,为什么又要定义和Collection相同的接口,其实List也有这个问题?答案是先有Set、List,而后有Collection。这里也要单独说下AbsractSet,因为它的equals是根据元素是否包含在集合中,两个Set元素相同,则他们相等

public abstract class AbstractSet<E> 
    extends AbstractCollection<E> implements Set<E> {
    public boolean equals(Object obj) {
        if(obj == this) {
            return true;
        } else if(!(obj instanceof Set)) {
            return false;
        }
        Collection collection = (Collection) obj;
        if(collection.size() != size()) {
            return false;
        }
        //前面都是基本的对象比较,这里只是添加了长度比较
        try {
            //关键点在这里,如果obj包含于当前this对象,则也会返回true
            return containAll(collection);
        } catch(ClassCastException classCastException) {
            return false;           
        } catch(NullPointerException nullPointException) {
            return false;
        }
    }
    //... ... ...
}

HashSet、TreeSet

在Set集合的实际使用中使用最广泛的就是这两个类,它们分别代表着无序和有序集合。它们内部实现都依赖于Map,并且由Map的Key来充当容器,key所对应的value为一个固定的Object对象PRESENT常量。Set保证不重复的秘诀就在于添加进来的对象的Hash值,它们在Map中进行存放时,Map会先检测key是否已经存在。TreeSet保证顺序的秘诀是TreeMap实现了红黑树结构,根据Comparator比较Key的大小来决定它们的存放顺序,并不是由存放先后顺序决定

public class HashSet<E> extends AbstractSet<E>
    implements Set<E>, Cloneable, Seriablizable {
    private transient HashMap map;
    private static final Object PRESENT = new Object();

    public HashSet() {
        map = new HashMap
    }
    //... ...
}

public class TreeSet<E> extends AbstractSet<E>
    implements SortedSet<E>, Cloneable, Serializable {
    private transient SortedMap m;
    private transient Set keySet;
    private static final Object PRESENT = new Object();

    public TreeSet(SortedMap m) {
        this.m = m;
        keySet = m.keySet();
    }
    //... ...
}

SynchronizedSet、SynchronizedSortedSet

在多线程环境下将Set作为共享数据时,HashSet、TreeSet的API不能保证线程安全,所以需要线程安全的Set实现类,而SynchronizedSet、SynchronizedSortedSet则是该环境下的产物。它们的内部实现都是锁定this或者一个共享的Object对象来保证线程安全。

static class SynchonizedSet<E> 
    extends SynchronizedCollection<E> implements Set<E> {
    public boolean equals(Object obj) {
        synchronized(mutex) {
            return c.equals(obj);
        }
    }
    //... ...
}
static class SynchronizedSortedSet<E>
    extends SynchronizedSet<E> implements SortedSet<E> {
    private SortedSet ss;
    public Comparator  comparator() {
        synchronized(mutex) {
            return ss.comparator();
        }
    }
}

CopyOnWriteArraySet

它是一个有序Set集合,根据存放元素的先后顺序来存放,而不同于TreeSet根据Comparator比较值决定顺序。此外,CopyOnWriteArrayList底层由System.copyarray来实现增add、删remove操作。对CopyOnWriteArraySet结构进行修改将增加内存消耗,因为它每次都是全量拷贝当前数组到新创建的数组,频繁的修改会造成内存中驻留过多的数组对象待回收。但它也保证了Set集合的修改不会抛出ConcurrentModificationException异常,也即对结构的修改不会彼此影响。

public class CopyOnWriteArraySet<E> 
    extends AbstractSet<E> implements Serializable {
    private final CopyOnWriteArrayList al;

    public CopyOnWriteArraySet() {
        al = new CopyOnWriteArrayList();
    }


}

结论

Set集合是无序、不重复和可扩展的Collection衍生类,它存放的是具有相同特性的一组对象。在实现方式上,HashSet、TreeSet都依赖于Map集合,而线程安全上SynchronizedSet、SynchronizedSortedSet都锁定内部对象mutex,在结构修改上保证操作的原子性,CopyOnWriteArraySet保证每次操作都是整个容器进行拷贝,避免fail-fast异常。

你可能感兴趣的:(java数据结构)