`
wenshao
  • 浏览: 271670 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

定制化高效使用Map的一些经验技巧

阅读更多
Map是一种非常用的数据结构,在一些底层框架或者效率十分关键的地方也是十分常用的。我写这篇文章的意图就是把我关于高效使用map的一些经验技巧写下来,当然其中我的一些观点可能不对,如果有朋友发现有错误的地方,欢迎指正。

在Java中Map是什么呢?先说HashMap,java.util.HashMap这个类,就是一个链表数组,最简单的理解就是把key的hashCode % len得到所在链表的位置,然后在链表上挨个查找。

这个代码摘抄自JDK 6的java.util.HashMap,为了方便说明问题,有所删减。其中一些关键点,我都已经注释说明
public class HashMap<K,V> {
	// Entry是一个很标准的链表结构
	static class Entry<K,V> {
		final K key;
		V value;
		Entry<K,V> next;
		final int hash;
	}

	transient Entry[] table; // table就是一个链表数组
	transient int size;
	
   public HashMap(int initialCapacity) {
        // 注意,这里是性能的一个很关键地方,如果自行编写HashMap时,table.length不是2的n次方,性能会很容易很糟糕。
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;

        table = new Entry[capacity];
    }
	
	// get方法所做的事情包括hash、indexFor、链表遍历、链表中每个Entry.key的相等比较
	public V get(Object key) {
	    int hash = hash(key.hashCode()); // 
		int index = indexFor(hash, table.length); 
	    for (Entry<K,V> e = table[index]; e != null; e = e.next) { // 链表遍历
	        Object k;
	        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
	            return e.value;
			}
	    }
	    return null;
	}
	
    public V put(K key, V value) {
        int hash = hash(key.hashCode());
        int index = indexFor(hash, table.length);
        for (Entry<K,V> e = table[index]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }
	
    void addEntry(int hash, K key, V value, int bucketIndex) {
		Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        if (size++ >= threshold) {
            resize(2 * table.length); // 多个线程并发访问时,resize会调用transfer方法,而transfer方法会在某种情况下产生死循环。
		}
    }

	static int hash(int h) {
	    h ^= (h >>> 20) ^ (h >>> 12);
	    return h ^ (h >>> 7) ^ (h >>> 4);
	}

	static int indexFor(int h, int length) {
	    return h & (length-1); // 这个比取模运算%速度快。
	}
}

通过上面的代码和注释,我们基本能够了解HashMap是干啥的,他是一个很简单很常用的数据结构,本身HashMap的性能很好,但是某些场景下,我们还是对HashMap做定制化的处理,使得其更高效。

例如Key是int的场景,专门编写IntHashMap能够获得更高效的性能,中间能够减少Integer对象的产生,减轻GC负担,同时,hash函数和遍历时的equals也能省下不少动作。一个性能比较数据如下:

测试的代码:
final int COUNT = 1000 * 10;
final int loopCount = 10000;
HashMap<Object, Object> map = new HashMap<Object, Object>(); // IntHashMap测试时相对应是IntHashMap

for (int i = 0; i < loopCount; ++i) {
	for (int j = 0; j < COUNT; ++j) {
		if (map.get(j) == null) {
			map.put(j, value);
		}
	}
}


结果:
Map类型耗时YoungGCFullGC
IntHashmap<Object>5,43700
Hashmap<Integer, Object>13,3122510


从结果来看,使用原生类型int替代Integer作为key,性能翻倍。

HashMap的性能是很好的,但不是线程安全的,最恶劣的并发问题就是table的resize时产生死循环。为了线程安全,我们通常需要使用ConcurrentHashMap,ConcurrentHashMap缺省能够支持16个并发写,而且不会产生令人十分讨厌的ConcurrentModificationException。可是ConcurrentHashMap的性能并不好,如上面的测试场景,测试性能数据如下:
Map类型耗时YoungGCFullGC
IntHashmap<Object>5,43700
Hashmap<Integer, Object>13,3122510
ConcurrentIntHashmap<Object> 21,45200
ConcurrentHashmap<Integer, Object>37,62414090


通过测试数据看,ConcurrentHashMap的get性能要比HashMap性能要差很多,3倍多的差距。

有没有办法做到线程安全,但是get性能接近HashMap呢?答案是肯定的!ConcurrentHashMap其实就是一个Segment数组,每个Segment的功能类似一个HashMap,Segment是线程安全的,一个ConcurrentHashMap缺省包含16个Segment,所以支持16个并发写入,但大多数场景我们并不需要并发写入,需要的是线程安全并且高效查找。那么思路就很明确了,把ConcurentHashMap的Segement拿出来,修改成一个HashMap,性能如何?测试数据补上:

Map类型耗时YoungGCFullGC说明
IntHashmap<Object>5,43700 参考HashMap修改而成
Hashmap<Integer, Object>13,3122510
ConcurrentIntHashmap<Object> 21,45200参考ConcurrentHashMap修改而成
ConcurrentHashmap<Integer, Object>37,62414090
FastConcurrentIntHashmap<Object>5,70300参考ConcurrentHashMap.Segment修改而成
FastConcurrentHashmap<Integer, Object>12,4992250参考ConcurrentHashMap.Segment修改而成


从数据来看,FastConcurrentIntHashmap的性能非常好,接近IntHashMap了,FastConcurrentHashmap<Integer, Object>的性能则比HashMap速度还快一点点,可能是ConcurrentHashMap.Segement的实现更高效吧。

总结一下,我们可以参考HashMap编写IntHashMap,参考ConcurrentHashMap编写ConcurrentIntHashMap,参考ConcurrentHashMap.Segment编写专门针对读取优化的FastConcurrentHashMap,从而在特别场景下获得更快的性能。

同理,我们也可以参考HashMap和ConcurrentHashMap编写相应的CharArrayHashMap和CharArrayConcurrentHashMap,在特别场景下,能够获得比HashMap<String, Object>以及ConcurrentHashMap<String, Object>更好的性能。
20
1
分享到:
评论
14 楼 DarrenD 2016-08-16  
 
13 楼 cfl520 2012-07-09  
int key的为什么要用map
用arrayList 或者数据效率应该更好啊?
12 楼 bitray 2012-04-01  
int map怎么开发的?
11 楼 t0591 2011-11-26  
特殊情况,使用应该非常好
10 楼 Arno 2011-08-26  
      

      

      

           

      
       

      
9 楼 chenchao051 2011-06-12  
写的相当好
8 楼 jojo_java 2011-02-18  
                   
7 楼 jojo_java 2011-02-18  
                
6 楼 wenshao 2011-02-16  
NanguoCoffee 写道
这么牛?
Doug Lea费那么大劲才搞出来的ConcurrentHashMap,就直接被你的FastConcurrentHashMap击败了?

Doug Lea还是别称为世上唯一“懂得”并发的少数几个人之一吧。


不是击败,只是定制化使用ConcurrentHashMap,把ConcurrentHashMap.Segement抽取出来改造成FastConcurrentHashMap。

ConcurrentHashMap缺省支持同时16个并发写入,这对于大多数使用场景是不需要的。
5 楼 NanguoCoffee 2011-02-16  
这么牛?
Doug Lea费那么大劲才搞出来的ConcurrentHashMap,就直接被你的FastConcurrentHashMap击败了?

Doug Lea还是别称为世上唯一“懂得”并发的少数几个人之一吧。
4 楼 javantsky 2011-02-12  
ConcurrentHashMap.Segment编写专门针对读取优化的FastConcurrentHashMap,从而在特别场景下获得更快的性能。

读取优化?

既然是安全的怎么读取优化,初始化后map数据后就只读?那还concurrent个毛呀,直接用hashmap不就完了么
3 楼 chenzan2010 2011-02-10  
2 楼 jsdit 2011-01-26  
ak121077313 写道
确实不错 int map的想法 lz的int map是怎么写的?

apache common lang的jar包里面有intHashMap
1 楼 ak121077313 2011-01-26  
确实不错 int map的想法 lz的int map是怎么写的?

相关推荐

    Eb163MapEditor

    2. **开源代码**:EB163 MapEditor的源代码完全开放,这意味着开发者可以查看、学习并修改其内部逻辑,根据自己的需求进行定制化开发,极大地扩展了编辑器的功能性和适应性。 3. **高效编辑**:编辑器提供了丰富的...

    tileMap.rar

    通过调整这些参数,可以实现各种定制化的地图效果。 六、TileMap的高级功能 1. **Collider Generation**:Unity可以自动为TileMap生成碰撞器,使游戏对象能够与地图进行交互。 2. **Parallax Effect**:通过设置...

    googlemap buddy 地图截取工具,以前的ripper不好用了

    该工具的主要特点是能够灵活地调整抓取的地理范围和图像质量,让用户可以定制化地获取地图数据。与传统在线浏览地图相比,GoogleMap Buddy提供了更高效、更便捷的解决方案。 首先,让我们来看看GoogleMap Buddy的...

    地质制图Geomap

    《地质制图软件GeoMap3.6:精准坐标定位与多坐标系统应用详解》 在地学研究和地质勘查工作中,精准的地理位置信息是至关...对于地质行业的专业人士来说,掌握GeoMap的使用技巧,无疑会提高工作效率,提升工作质量。

    Autodesk Map 3D 2005 官方中文教程

    #### 五、Autodesk Map 3D 2005 使用技巧 - **高效数据导入**:了解如何快速将不同来源的数据导入到Autodesk Map 3D中,并保持数据的完整性和一致性。 - **地图样式自定义**:掌握如何根据项目需求调整地图的样式,...

    Map-master_mapmaster_web地图_百度地图_

    在标题中提到的“Map-master”,可能是指一个用于管理或控制地图显示的框架或工具,帮助开发者更高效地集成和定制百度地图。在实际项目中,这样的工具能够简化地图操作,提供更多的自定义选项,例如添加标记、热力图...

    MapObject入门教程

    MapObject入门教程主要针对的是那些想要在ArcGIS平台上进行二次开发的用户,它是一个核心的开发工具,使得开发者能够利用GIS(地理信息系统)的功能来构建定制化的应用程序。MapObjects是Esri公司提供的一种COM...

    使用Webpack的提示和技巧

    本文将深入探讨一些使用Webpack的提示和技巧,以提升开发效率和项目性能。 1. **配置文件理解**: Webpack 的核心在于其配置文件 `webpack.config.js`。配置文件允许你定义输入、输出、加载器、插件等一系列参数,...

    Vim使用技巧

    Vim是一款功能强大的文本编辑器,广泛应用于Unix、Linux及MAC OS...通过深入学习和实践,用户可以将Vim打造成为个人定制化极高的编程环境。随着熟练度的提升,Vim的快速编辑能力将极大地提高编程和文本处理的工作效率。

    AutoCAD Map 3D中文教程

    ### AutoCAD Map 3D中文教程知识点概览 #### 一、AutoCAD Map 3D简介 ...通过以上知识点的学习和实践,您将能够更好地掌握AutoCAD Map 3D的应用技巧,提升工作效率,并解决实际工作中遇到的各种问题。

    map/reduce template

    在描述中提到的链接虽然没有提供具体内容,但通常在ITeye这样的技术博客平台上,博主可能会分享关于如何使用MapReduce模板、解决实际问题或者优化MapReduce作业的技巧和经验。 标签“源码”提示我们可能会涉及...

    DORADO组件使用技巧.pdf

    ### DORADO组件使用技巧详解 #### 一、概述 DORADO是BSTEK公司推出的一款专注于企业级应用开发的框架,特别适用于构建复杂的Web应用程序。其核心优势在于丰富的组件库,这些组件不仅覆盖了基本的UI元素,还提供了...

    Visual C++高级编程技巧(3)

    模板元编程是一种在编译时执行计算的技术,可以生成高效且高度定制化的代码。尽管复杂,但一旦掌握,可以极大地提升代码的灵活性和效率。了解并实践模板元编程,如Boost.MPL库,是提升C++编程技能的高阶技巧。 六、...

    Google搜索技巧终极收集 - 101个Google技巧

    以上是Google搜索的一些基础和高级技巧,通过这些技巧的应用,用户可以更高效地利用Google搜索功能,找到所需的信息。无论是寻找特定网站的信息,还是进行复杂的数据分析,Google都提供了丰富的工具和方法来帮助用户...

    自定义map实现java的hashmap

    在Java编程中,HashMap是一个非常重要的数据结构,它实现了Map接口,提供了键值对的存储功能,具有快速存取和高效查找的特点。HashMap基于哈希表(也称为散列表)原理,通过键对象的哈希码来定位元素,进而实现O(1)...

    开发高效的hive程序

    1. SQL编写技巧:避免使用全表扫描,尽量使用分区和索引来定位数据;使用JOIN时,选择正确的JOIN类型(如LEFT JOIN、RIGHT JOIN、INNER JOIN),并确保大表在JOIN操作中位于右侧。 2. 数据倾斜处理:数据倾斜会导致...

    java面试问题小集

    由于 Map 是接口,不能直接实例化,通常我们会使用它的实现类,如 HashMap、TreeMap 或者 LinkedHashMap 来创建 Map 对象。 4. **Map 和 HashMap 的区别** - Map 是接口,HashMap 是其实现类;HashMap 支持快速...

    高德地图demo

    首先,Vue-AMap是高德地图为Vue.js框架定制的插件,它允许开发者轻松地在Vue项目中集成高德地图的功能,如定位、标注、路线规划等。这个demo的实现主要包括以下几个关键点: 1. **高精度定位**:当用户进入页面时,...

Global site tag (gtag.js) - Google Analytics