数组:固定长度的对象容器,存储基础类型。[]
集合:动态长度的对象容器,只能存储对象。 collection
一、List
ArrayList
底层实现是Object数组,当对象数组大小达到最大的时候,就会自动扩展。
集合大小增长速度,+原来大小的1/2, 右移>> 1
用于查询较多的情况
LinkedList
有序的列表,底层实现是链表,不需要增长,自动增长。
所以经常用于增删操作较多的情况
Vector
就是ArrayList的线程安全实现。
在修改List的方法都加了同步关键字
Collections.synchronizedList(list) 把list对象封装一层synchronized方法,使其线程安全。
CopyOnWiriteArrayList 利用以下两个常识,达到读写分离的效果,实现快速读,写一致的效果。
1、JAVA中“=”操作只是将引用和某个对象关联,假如同时有一个线程将引用指向另外一个对象,一个线程获取这个引用指向的对象,那么他们之间不会发生ConcurrentModificationException,他们是在虚拟机层面阻塞的,而且速度非常快,几乎不需要CPU时间。
2、JAVA中两个不同的引用指向同一个对象,当第一个引用指向另外一个对象时,第二个引用还将保持原来的对象。
CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。
Collection 和 Collections 的区别
Collection是接口,集合类的根接口
Collections是工具类。
二、 Map
1. HashMap
JDK7:
底层维护的时一个Entry对象数组。
transient Entry<K, V>[] table;
key的hash值决定value的位置,hash相同的值存在同一地址,用链式相连。
hash冲突多时,HashMap退化成链表.
JDK8:
为了解决hash冲突数量过多的问题,在HashMap里引入红黑二叉树,当链表数据量达到某个阀值(8)就会切换程红黑二叉树。
Entry数组变成了Node数组,原因,是为了实现红黑二叉树。
当冲突链表值> 8-1 时, 会转为红黑二叉树实现。
当冲突<= 6时,会切回链表实现。
精髓:
高16位于低16位 ^ 异或运算,缩短hash值且充分利用hash值,使分布更均匀。
0.75 为什么是
2. LinkedHashMap 是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。
3. TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的
使用各种Map的情况:
一般情况下,我们用的最多的是HashMap,在Map 中插入、删除和定位元素,HashMap 是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。如果需要输出的顺序和输入的相同,那么用LinkedHashMap 可以实现,它还可以按读取顺序来排列.
HashMap是一个最常用的Map,它根据键的hashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为NULL,允许多条记录的值为NULL。
4. HashTable是HashMap线程安全的实现,key不允许未NULL。
ConcurrentHashMap 同步的优化类,比HashTable更高效。
分段锁技术,分成多个Segment,Segment存放HashEntry数组。
查询Segment, 使用双层Hash减少hash冲突。
Hash值最大是32位,二次Hash是无符号位移28位,使高4位参与hash运算中,减少Hash值发生冲突。
ConcurrentHashMap get 方法是非常高效的,
Segment的get操作实现非常简单和高效。先经过一次再哈希,然后使用这个哈希值通过哈希运算定位到segment,再通过哈希算法定位到元素,代码如下:
public V get(Object key) {
int hash = hash(key.hashCode());
return segmentFor(hash).get(key, hash);
}
get操作的高效之处在于整个get过程不需要加锁,除非读到的值是空的才会加锁重读,我们知道HashTable容器的get方法是需要加锁的,那么ConcurrentHashMap的get操作是如何做到不加锁的呢?原因是它的get方法里将要使用的共享变量都定义成volatile,如用于统计当前Segement大小的count字段和用于存储值的HashEntry的value。定义成volatile的变量,能够在线程之间保持可见性,能够被多线程同时读,并且保证不会读到过期的值,但是只能被单线程写(有一种情况可以被多线程写,就是写入的值不依赖于原值),在get操作里只需要读不需要写共享变量count和value,所以可以不用加锁。之所以不会读到过期的值,是根据java内存模型的happen before原则,对volatile字段的写入操作先于读操作,即使两个线程同时修改和获取volatile变量,get操作也能拿到最新的值,这是用volatile替换锁的经典应用场景。
transient volatile int count;
volatile V value;
三、 Set(是用Map实现的, 以值为Map的key存储, 所以了解Map基本Set的原理都差不多)
HashSet 散列的set
LinkedHashSet 有序的set
TreeSet 树形Set,可以排序,非线程安全的
ConcurrentSkipListSet 线程安全的跳表Set
同步情况
copyOnWriteArraySet 读写分离的Set实现
ConcurrentSkipSet
四、 Tree
Java Tree实现可以参考TreeMap
五、 Queue 队列
1. LinkedList 这个是双向链表。
2. ArrayQueue 数据队列 先进先出
3. PriorityQueue 二叉树实现的优先队列。
线程安全队列
PriorityBlockingQueue 优先级阻塞队列
基于优先级的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定),但需要注意的是PriorityBlockingQueue并不会阻塞数据生产者,而只会在没有可消费的数据时,阻塞数据的消费者。因此使用的时候要特别注意,生产者生产数据的速度绝对不能快于消费者消费数据的速度,否则时间一长,会最终耗尽所有的可用堆内存空间。在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁。
DelayQueue 延迟队列
元素只有延迟指定时间到了,才能从队列中取出。DelayQueue也是一个没有大小限制的队列,所以插入数据永远不会被阻塞,而且只有获取数据操作才会阻塞。
使用场景比较少,例子:管理一个超时未响应的连接队列???。
ArrayBockingQueue 数组阻塞队列
基于定长的数组的阻塞队列,内部维护一个定长的数据缓存队列,擦入和拉出都是同一个对象锁,所以不存在同时擦入拉出。
因为增益不大,和增加代码复杂度没有把锁分离。
LinkedBlockingQueue 线性阻塞队列
基于链表的阻塞队列,该队列为链表结构,内部维护一个数据缓冲队列(链表结构),缓冲区满载,默认大小是Integer.MAX_VALUE, 这里存在一个内存溢出的风险。
SynchronousQueue
阻塞队列: 它一种阻塞队列,其中每个 put 必须等待一个 take,反之亦然。
队列内必须有东西,才能拿,否则阻塞。
详细内容:https://www.cnblogs.com/geningchao/p/6638781.html
网友评论