集合框架—常用的map集合
1、Collections.synchronizedMap()
实现上在调用map所有方法时,都对整个map进行同步,而ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。所以,只要要有一个线程访问map,其他线程就无法进入map,而如果一个线程在访问ConcurrentHashMap某个桶时,其他线程,仍然可以对map执行某些操作。这样,ConcurrentHashMap在性能以及安全性方面,明显比Collections.synchronizedMap()更加有优势。同时,同步操作精确控制到桶,所以,即使在遍历map时,其他线程试图对map进行数据修改,也不会抛出ConcurrentModificationException。
ConcurrentHashMap从类的命名就能看出,它必然是个HashMap。Collections.synchronizedMap()可以接收任意Map实例作为封装。
=====================================================================================
下面的例子显示java.util.Collections.synchronizedMap()方法的使用
package com.yiibai;
import java.util.*;
public class CollectionsDemo {
public static void main(String[] args) {
// create map
Map<String,String> map = new HashMap<String,String>();
// populate the map
map.put("1","TP");
map.put("2","IS");
map.put("3","BEST");
// create a synchronized map
Map<String,String> synmap = Collections.synchronizedMap(map);
System.out.println("Synchronized map is :"+synmap);
}
}
现在编译和运行上面的代码示例,将产生以下结果。
Synchronized map is :{3=BEST, 2=IS, 1=TP}
也不会抛出ConcurrentModificationException。
===============================================================================
2、ConcurrentHashMap
1:线程安全,
2:Iterator的迭代方式采用的是fail-safe的错误机制
ConcurrentHashMap在线程安全的基础上提供了更好的写并发能力,但同时降低了对读一致性的要求,ConcurrentHashMap的设计与实现非常精巧,大量的利用了volatile,final,CAS等lock-free技术来减少锁竞争对于性能的影响.
3:而ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。
4:同时,同步操作精确控制到桶,所以,即使在遍历map时,其他线程试图对map进行数据修改,也不会抛出ConcurrentModificationException。
ConcurrentHashMap采用了分段锁的设计,只有在同一个分段内才存在竞态关系,不同的分段锁之间没有锁竞争。相比于对整个Map加锁的设计,分段锁大大的提高了高并发环境下的处理能力。但同时,由于不是对整个Map加锁,导致一些需要扫描整个Map的方法(如size(), containsValue())需要使用特殊的实现,另外一些方法(如clear())甚至放弃了对一致性的要求(ConcurrentHashMap是弱一致性的,
ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。
源代码:
static final class Segment<K,V> extends ReentrantLock implements Serializable{
并发度
并发度可以理解为程序运行时能够同时更新ConccurentHashMap且不产生锁竞争的最大线程数,实际上就是ConcurrentHashMap中的分段锁个数,即Segment[]的数组长度。ConcurrentHashMap默认的并发度为16,但用户也可以在构造函数中设置并发度。当用户设置并发度时,ConcurrentHashMap会使用大于等于该值的最小2幂指数作为实际并发度(假如用户设置并发度为17,实际并发度则为32)。运行时通过将key的高n位(n = 32 – segmentShift)和并发度减1(segmentMask)做位与运算定位到所在的Segment。segmentShift与segmentMask都是在构造过程中根据concurrency level被相应的计算出来。
如果并发度设置的过小,会带来严重的锁竞争问题;如果并发度设置的过大,原本位于同一个Segment内的访问会扩散到不同的Segment中,CPU cache命中率会下降,从而引起程序性能下降。(文档的说法是根据你并发的线程数量决定,太多会导性能降低)
JDK8中的实现
ConcurrentHashMap在JDK8中进行了巨大改动,很需要通过源码来再次学习下Doug Lea的实现方法。
它摒弃了Segment(锁段)的概念,而是启用了一种全新的方式实现,利用CAS算法。它沿用了与它同时期的HashMap版本的思想,底层依然由"数组"+链表+红黑树的方式思想(JDK7与JDK8中HashMap的实现),但是为了做到并发,又增加了很多辅助的类,例如TreeBin,Traverser等对象内部类。
重要的属性
首先来看几个重要的属性,与HashMap相同的就不再介绍了,这里重点解释一下sizeCtl这个属性。可以说它是ConcurrentHashMap中出镜率很高的一个属性,因为它是一个控制标识符,在不同的地方有不同用途,而且它的取值不同,也代表不同的含义。
- 负数代表正在进行初始化或扩容操作
- -1代表正在初始化
- -N 表示有N-1个线程正在进行扩容操作
- 正数或0代表hash表还没有被初始化,这个数值表示初始化或下一次进行扩容的大小,这一点类似于扩容阈值的概念。还后面可以看到,它的值始终是当前ConcurrentHashMap容量的0.75倍,这与loadfactor是对应的。
总结
JDK6,7中的ConcurrentHashmap主要使用Segment来实现减小锁粒度,把HashMap分割成若干个Segment,在put的时候需要锁住Segment,get时候不加锁,使用volatile来保证可见性,当要统计全局时(比如size),首先会尝试多次计算modcount来确定,这几次尝试中,是否有其他线程进行了修改操作,如果没有,则直接返回size。如果有,则需要依次锁住所有的Segment来计算。
jdk7中ConcurrentHashmap中,当长度过长碰撞会很频繁,链表的增改删查操作都会消耗很长的时间,影响性能,所以jdk8 中完全重写了concurrentHashmap,代码量从原来的1000多行变成了 6000多 行,实现上也和原来的分段式存储有很大的区别。
主要设计上的变化有以下几点:
- 不采用segment而采用node,锁住node来实现减小锁粒度。
- 设计了MOVED状态 当resize的中过程中 线程2还在put数据,线程2会帮助resize。
- 使用3个CAS操作来确保node的一些操作的原子性,这种方式代替了锁。
- sizeCtl的不同值来代表不同含义,起到了控制的作用。
至于为什么JDK8中使用synchronized而不是ReentrantLock,我猜是因为JDK8中对synchronized有了足够的优化吧。
3、LinkedHashMap
是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。
Map<Integer,String> map = new LinkedHashMap<Integer,String>();
map.put(6, "apple");
map.put(3, "banana");
map.put(2,"pear");
for (Iterator it = map.keySet().iterator();it.hasNext();)
{
Object key = it.next();
System.out.println( key+"="+ map.get(key));
}
运行结果如下:
*************************LinkedHashMap*************
6=apple
3=banana
2=pear
4、TreeMap
1:TreeMap 类不仅实现了 Map 接口,还实现了 Map 接口的子接口 java.util.SortedMap。
2:TreeMap 类中不允许键对象为 null 或是基本数据类型,这是因为 TreeMap 中的对象必须是可排序的(即对象需要实现 java.lang.Comparable 接口)
3:在创建 TreeMap 对象时,如果使用参数为空的构造方法,则根据 Map 对象的 key 进行排序;如果使用参数为 Comparator 的构造方法,则根据 Comparator 进行排序。
4:在添加、删除和定位映射关系上,TreeMap类要比HashMap类的性能差一些,
5:在需要排序时,利用现有的 HashMap,创建一个 TreeMap 类型的实例
Java代码
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.TreeMap;
- public class TestCollection {
- public static void main(String[] args) {
- System.out.println("开始:");
- Person person1 = new Person("马先生", 220181);
- Person person2 = new Person("李先生", 220193);
- Person person3 = new Person("王小姐", 220186);
- Map<Number, Person> map = new HashMap<Number, Person>();
- map.put(person1.getId_card(), person1);
- map.put(person2.getId_card(), person2);
- map.put(person3.getId_card(), person3);
- // HashMap
- System.out.println("HashMap,无序:");
- for (Iterator<Number> it = map.keySet().iterator(); it.hasNext();) {
- Person person = map.get(it.next());
- System.out.println(person.getId_card() + " " + person.getName());
- }
- // TreeMap
- System.out.println("TreeMap,升序:");
- //创建 TreeMap 时,如果使用参数为空的构造方法,则根据 Map 对象的 key 进行排序
- TreeMap<Number, Person> treeMap = new TreeMap<Number, Person>();
- //将指定映射中的所有映射关系复制到此映射中。
- treeMap.putAll(map); for (Iterator<Number> it = treeMap.keySet().iterator(); it.hasNext();) {
- Person person = treeMap.get(it.next());
- System.out.println(person.getId_card() + " " + person.getName());
- }
- System.out.println("TreeMap,降序:");
- //Collections.reverseOrder()返回一个比较器,它强行逆转指定比较器顺序
- //如果使用参数为 Comparator 的构造方法,则根据 Comparator 进行排序。
- TreeMap<Number, Person> treeMap2 =
- new TreeMap<Number, Person>(Collections.reverseOrder());
- treeMap2.putAll(map);
- for (Iterator it = treeMap2.keySet().iterator(); it.hasNext();) {
- Person person = (Person) treeMap2.get(it.next());
- System.out.println(person.getId_card() + " " + person.getName());
- }
- System.out.println("结束!");
- }
- }
集合框架—常用的map集合的更多相关文章
- 牛客网Java刷题知识点之Java 集合框架的构成、集合框架中的迭代器Iterator、集合框架中的集合接口Collection(List和Set)、集合框架中的Map集合
不多说,直接上干货! 集合框架中包含了大量集合接口.这些接口的实现类和操作它们的算法. 集合容器因为内部的数据结构不同,有多种具体容器. 不断的向上抽取,就形成了集合框架. Map是一次添加一对元素. ...
- 理解java集合——集合框架 Collection、Map
1.概述: @white Java集合就像一种容器,可以把多个对象(实际上是对象的引用,但习惯上都称对象)"丢进"该容器中. 2.Java集合大致可以分4类: @white Set ...
- 07java进阶——集合框架3(Map)
1.映射表(Map) 1.1基本概念 1.2Map中常用的方法 package cn.jxufe.java.chapter7; import java.util.HashMap; import jav ...
- 浅谈集合框架三、Map常用方法及常用工具类
最近刚学完集合框架,想把自己的一些学习笔记与想法整理一下,所以本篇博客或许会有一些内容写的不严谨或者不正确,还请大神指出.初学者对于本篇博客只建议作为参考,欢迎留言共同学习. 之前有介绍集合框架的体系 ...
- Java集合框架List,Map,Set等全面介绍
Java集合框架的基本接口/类层次结构: java.util.Collection [I]+--java.util.List [I] +--java.util.ArrayList [C] +- ...
- 【转】Java集合框架List,Map,Set等全面介绍
原文网址:http://android.blog.51cto.com/268543/400557 Java Collections Framework是Java提供的对集合进行定义,操作,和管理的包含 ...
- java的集合框架set 和map的深入理解
Java的集合框架之Map的用法详解 Map有两种比较常用的实现:HashMap 和 TreeMap. HashMap: HashMap 也是无序的,也是按照哈希编码来排序的,允许使用null 值和n ...
- 34、Java集合框架List,Map,Set等全面介绍(转载)
Java Collections Framework是Java提供的对集合进行定义,操作,和管理的包含一组接口,类的体系结构. Java集合框架的基本接口/类层次结构: java.util.C ...
- java学习——集合框架(泛型,Map)
泛型: ... Map:一次添加一对元素.Collection 一次添加一个元素. Map也称为双列集合,Collection集合称为单列集合. 其实map集合中存储的就是键值对. map集合中必须保 ...
随机推荐
- myisam innodb 次级 索引的区别
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址.下图是MyISAM索引的原理图: 这里设表一共有三列,假设我们以Col1为主键,则上图是一个MyISAM表的主索 ...
- Python拷贝文件脚本
author : headsen chen date : 2018-12-06 17:56:58 copy_file.py #!/usr/bin/env python from sys import ...
- iOS8跳转到系统设置页
版权声明:本文为博主原创文章,未经博主允许不得转载. 大家都知道,在iOS5.0时时可以跳转到系统的设置页的.但是在5.1之后就不可以了. 刚才研究了下这个问题,发现只有iOS8可以跳转到系统设置里自 ...
- jenkins中windows节点设置开机自启动slave-agent
做web UI自动化时,为了提高效率,用了多台windows节点来跑自动化,但slave-agent每次在关机后都得手工启动,麻烦,网上看到了一系列说启动任务中,感觉还是不考虑,这里使用windows ...
- unity3d的优化场景技术LOD+IOC
一.unity3d的优化场景技术 LOD+IOC 遮挡剔除(occlusion culling)其实就是在摄像机范围内的物体才被渲染出来,没有在视野范围内的,统统关掉渲染,这样能让性能大大提高. I ...
- github相关资料记录
github官方配ssh api:https://help.github.com/articles/generating-ssh-keys 简书hexo静态博客搭建:http://www.jiansh ...
- C++ 标准输出cout与printf
C++标准输出cout与printf都可以,printf用法更死板一些. #include <iostream> int main(int argc, char** argv) { usi ...
- mui+vue+photoclip做APP头像裁剪上传
做APP由于项目需要,需要做用户头像上传的功能,头像上传包括拍照和相册选择图片进行上传,这里使用的技术是mui封装的plus,在进行图片裁剪的时候,使用的是photoclip来进行裁剪,由于个人在使用 ...
- js模拟点击打开超链接
js模拟点击打开超链接,页面上有一些锚文本,如果用 JS 批量在新窗口打开. jquery示例: <div class="link"> <a href=" ...
- 解决 Python shell 中 Delete/Backspace 键乱码问题
简述 进入 Python shell,按下 Delete/Backspace 键,会出现 ^H 字符.命令输入错误后只能从头开始,无法删除,让人很头疼.为了便于后期使用,分享一个一劳永逸的方式. 基本 ...