HashMap(允许null、不同步)
HashMap及与HashTable的比较:HashMap(允许null、不同步,数组+链表+红黑树),HashTable(不允许key和value为null,线程安全,数组+链表)
HashTable替代:ConcurrentHashMap,不允许null
再散列,桶数*2
tableSizeFor(),返回一个大于输入参数且最接近2的整数此幂的数。
用&实现取余,用&oldcap实现扩容时元素移动与否判断(移动oldcap或不动)
LinkedHashMap(允许null,不同步,有序)
可用来实现LRU算法(Least Recently Used最近最少使用)页面置换常用算法。
插入顺序与访问顺序:
access-ordered linked hash maps(LRU)和insertion-ordered hash maps(此时修改已有key的value不是结构性修改),默认是插入顺序
对于访问顺序,它是LRU(最近最少使用)算法的实现,要使用它要么重写LinkedListMap的几个方法(removeEldestEntry(Map.Entry<K,V> eldest重写它可以删除最久未被使用的元素)和afterNodeInsertion(boolean evict)新增时判断是否需要删除最久未被使用的元素),要么是扩展成LRUMap来使用,不然设置为访问顺序(access-ordered)的用处不大
散列表+双向链表
初始容量对遍历没有影响从内部维护的双链表的表头开始循环输出
在构建新节点时构建的是LinkedHashMap.Entry不再是Node(继承HashMap的Node节点)重写了newNode方法
多态是无处不在的(子类用父类的方法,子类重写了父类的部分方法即可达到不一样的效果)比如:LinkedHashMap并没有重写put方法,而put方法内部的newNode()方法重写了。LinkedHashMap调用父类的put方法,里面回调的是重写后的newNode(),从而达到目的!
TreeMap(红黑树,非同步,有序,key不为空)
containsKeys、get、put、remove时间复杂度为logn。
TreeMap实现有序要么就是外界传递进来Comparator对象(compare方法),要么就使用默认key的Comparable()接口(compareTo方法)(实现自然排序);
TreeMap有序是通过Comparator来进行比较的,如果comparator为null,那么就使用自然顺序。(如果在构造方法中传递了Comparator对象,那么就会以Comparator对象的方法进行比较。否则,则使用Comparable的compareTo(T o)方法来比较。)
- 如果使用的是compareTo(T o)方法来比较,key一定是不能为null,并且得实现了Comparable接口的。
- 即使是传入了Comparator对象(用compare比较),不用compareTo(T o)方法来比较,key也是不能为null的
modCount指结构性修改的次数。(遍历时可用于判断是否在遍历的同时更改了结构ConcurrentModificationException)
TreeMap遍历是使用EntryIterator这个内部类
- 由于底层是红黑树,那么时间复杂度可以保证为log(n)
- key不能为null,为null为抛出NullPointException的
- 想要自定义比较,在构造方法中传入Comparator对象,否则使用key的自然排序来进行比较
- TreeMap非同步的,想要同步可以使用Collections来进行封装
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f4GEffAa-1645971546430)(E:\note\image\Comparator.png)]
ConcurrentHashMap(1.8散列表+红黑树,不允许key或value为null)
只允许一个线程对散列表进行初始化。
get方法非阻塞。
Node节点是重写的,设置了volatile关键字修饰,致使它每次获取的都是最新设置的值。
JDK1.7的底层是:segments+HashEntry数组。
- Hashtable是在每个方法上都加上了Synchronized完成同步,效率低下。
- ConcurrentHashMap通过在部分加锁和利用CAS算法来实现同步。
CAS算法与volatile关键字(通过部分锁与CAS算法实现线程安全)
CAS(一种乐观锁)(比较与交换,Compare and swap),是一种有名的无锁算法。
有三个操作数(内存值V、旧的预期值A、要修改的新值B)
当且仅当预期值A和内存值V相等时,将内存值V修改为B,否则什么都不做。
当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值(A和内存值V相同时,将内存值V修改为B),而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试(否则什么都不做)
volatile仅仅用来保证该变量对所有线程的可见性,但不保证原子性。
不要将volatile用在getAndOperate场合,仅仅set或者get的场景是适合volatile的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KmBfdLa2-1645971546431)(E:\note\image\volatile.png)]
load到store之间是不安全的
volatile,Java内存模型将在写操作后插入一个写屏障指令,在读操作前插入一个读屏障指令。这意味着如果你对一个volatile字段进行写操作,你必须知道:1、一旦你完成写入,任何访问这个字段的线程将会得到最新的值。写入字段时所有线程都可以获得2、在你写入前,会保证所有之前发生的事已经发生,并且任何更新过的数据值也是可见的,因为内存屏障会把之前的写入值都刷新到缓存。强制刷新一次缓存
不能用于构建原子的复合操作,因此当一个变量依赖旧值时就不能使用volatile变量。
特别地,long型赋值不是原子操作,用volatile修饰后long型赋值可变为原子操作。
http://www.cnblogs.com/Mainz/p/3556430.html
https://www.cnblogs.com/Mainz/p/3546347.html
http://www.dataguru.cn/java-865024-1-1.html
ps://www.cnblogs.com/Mainz/p/3546347.html
http://www.dataguru.cn/java-865024-1-1.html