diff --git a/blogs/Java/口水话/集合源码.md b/blogs/Java/口水话/集合源码.md index 1beccde..928b416 100644 --- a/blogs/Java/口水话/集合源码.md +++ b/blogs/Java/口水话/集合源码.md @@ -90,3 +90,24 @@ HashSet 的源码还是很少的,它保证了每个元素是不会重复的。 #### TreeSet +TreeSet 大致的结构和 HashSet 相似,底层组合的是 TreeMap,所以继承了 TreeMap key 能够排序的功能,同时也具有 Set 不可重复的属性。 + +TreeSet 的 add 方法是调用 NavigableMap 的 put 方法的,它是一个接口,它的实现是在 TreeMap 中,也就是说,TreeSet 定义了接口的规范,TreeMap 负责去实现。 + +TreeSet 基本上不常用,一般需要不重复元素且能根据元素进行排序的时候,才会用到 TreeSet,使用时需要我们注意最好实现 Comparable 接口,这样方便底层的 TreeMap 根据 key 进行排序。 + +#### CopyOnWriteArrayList + +CopyOnWriteArrayList 是一个线程安全的 ArrayList,它是通过 synchronized + 数组拷贝 + volatile 关键字保证了线程安全。在它 add value 时,是先 synchronized 加锁保证同一时刻只有一个线程执行 add 方法,再拷贝一份新数组,把 value 加到新数组里面,最后再把新数组赋值给原数组。你可能会问了,这都加锁了,为啥不在原数组上面直接操作呢?原因主要有两个: + +1. volatile 关键字修饰的是数组,如果只是简单的在原数组上修改其中某几个元素的值,是无法触发可见性的,我们必须通过修改数组的内存地址才行,也就说要对数组进行重新赋值才行 +2. 在新的数组上进行拷贝,对老数组没有任何影响,只有新数组完全拷贝完成之后,外部才能访问到,降低了在赋值过程中,老数组数据变动的影响 + +remove 时和 add 相似,就不多说了。 + +CopyOnWriteArrayList 读的时候不需要加锁,适合读多写少的场景,但是每次变动数组,都会拷贝一份新数组,可能会加重 GC。 + +#### ConcurrentHashMap + +ConcurrentHashMap 是一个线程安全的 HashMap,它是不允许 key 或 value 为 null 的。它的底层也是使用数组 + 链表 + 红黑树来实现的, +