集合類不安全之Set

1、不安全的Set

上代碼:

    public static void main(String[] args) throws Exception {

        Set<String> set = new HashSet<>();

        for (int i = 0;i<30;i++){
            new Thread(()->{
                set.add(Thread.currentThread().getName());
                System.out.println(Thread.currentThread().getName()+"\t"+set);
            }).start();
        }
    }

//運行結果如下:多線程共同修改set集合,造成了併發修改異常
java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1445)
    at java.util.HashMap$KeyIterator.next(HashMap.java:1469)
    at java.util.AbstractCollection.toString(AbstractCollection.java:461)
    at java.lang.String.valueOf(String.java:2994)
    at java.lang.StringBuilder.append(StringBuilder.java:131)
    at juc.ContainerNotSafeDemo.lambda$main$0(ContainerNotSafeDemo.java:17)
    at java.lang.Thread.run(Thread.java:748)

2、安全的解決方式

使用CopyOnWriteArraySet解決

    public static void main(String[] args) throws Exception {

        Set<String> set = new CopyOnWriteArraySet<>();//new HashSet<>();

        for (int i = 0;i<30;i++){
            new Thread(()->{
                set.add(Thread.currentThread().getName());
                System.out.println(Thread.currentThread().getName()+"\t"+set);
            }).start();
        }
    }

關於寫時複製技術在這篇博客裏寫過,不再贅述。深入探索一下,看源碼:

public class CopyOnWriteArraySet<E> extends AbstractSet<E>   
        implements java.io.Serializable {
    private static final long serialVersionUID = 5457747651344034263L;

    private final CopyOnWriteArrayList<E> al;

    /**
     * Creates an empty set.
     */
    public CopyOnWriteArraySet() {            //無參構造方法
        al = new CopyOnWriteArrayList<E>();  //實際上創建的是CopyOnWriteArrayList
    }
    ....
}

3、關於HashSet的補充

HashSet底層是什麼?看源碼:

    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }

註釋的意思是:創建一個空的HashMap,初始容量是16,負載因子是0.75.

HashSet在add的時候傳的是一個值並不是<k,v>,比如下邊:

new HashSet<>().add("hello");

確定HashSet底層是HashMap嗎???

這是一個容易被忽略的地方,查看HashSet的add方法源碼:

    /**
     * Adds the specified element to this set if it is not already present.
     * 如果指定的元素尚未出現,則將其添加到此集合。
     * More formally, adds the specified element e to this set if
     * this set contains no element e2 such that
     * (e==null?e2==null:e.equals(e2)).
     * If this set already contains the element, the call leaves the set
     * unchanged and returns false
     * 如果該集合已經包含元素,則調用將不修改集合,返回false。
     * @param e element to be added to this set
     * @return <tt>true</tt> if this set did not already contain the specified
     * element
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

可以看到,add方法其實是給HashMap添加了Key,value是PRESENT,它是一個Object類型的常量。調用的還是map的add方法。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章