Java中如何使用Map集合类,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
Java中最长用的集合类是List和Map,List的具体实现包括 ArrayList 和 Vector,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象元素列表。List 适用于按数值索引访问元素的情形。
Map提供了一个更通用的元素存储方法,Map集合类用于存储元素对(称作“键”和“值”),其中每个键映射到一个值。从概念上而言可以将List看作是具有数值键的 Map。而实际上除了List和Map都在定义 java.util中外,两者并没有直接的联系。
Java核心类中有很多预定义的Map类,在介绍具体实现之前先了解一下Map接口本身,以便于清楚所有实现的共同点。Map接口定义可以分为四种类型的方法,两个最常用的方法equals(Object o)是比较指定对象与此Map的等价性,而hashCode()返回此Map的哈希码,可以将这Object的这两个方法覆盖,以正确比较Map对象的等价性。
Map定义了几个用于插入和删除元素的变换方法,clear()是从Map中删除所有数据,remove(Object key)是从Map中删除键和关联的值,put(Object key, Object value)将指定值与指定键相关联,putAll(Map t)将指定Map中的所有数据复制到此map。注意:putAll通常不必使用大量的put()调用更有效率,而putAll()存在的意义是除了用户迭代put()所执行的每个键值对添加到Map的算法外,还需迭代所传递的Map元素。putAll()在添加元素之前可以调整Map的大小。如果未调整Map的大小,则putAll()可能比预期的更有效。
Java中自带各种Map类,这些Map类可以归为三种类型:
1) 通用Map用于在应用程序中管理映射,通常在java.util程序包中实现
HashMap 是基于“拉链法”实现的散列表。底层采用“数组+链表”实现。
Hashtable 基于“拉链法”实现的散列表。
Properties 继承Map类
LinkedHashMap 是HashMap的一个子类,保存了记录的插入顺序
IdentityHashMap 哈希表实现Map接口,比较键(和值)时使用引用相等性代替对象相等性。
TreeMap 有序散列表,实现SortedMap接口,底层通过红黑树实现。
WeakHashMap 基于“拉链法”实现的散列表。
ConcurrentHashMap 采用了分段锁的设计,只有在同一个分段内才存在竞态关系,不同的分段锁之间没有锁竞争。
2) 专用Map通常不必亲自创建此类Map,而是通过某些其他类对其进行访问
java.util.jar.Attributes
javax.print.attribute.standard.PrinterStateReasons
java.security.Provider
java.awt.RenderingHints
javax.swing.UIDefaults
3) 用于帮助实现自己的Map类的抽象类
AbstractMap
这个类注意用于减少实现Map接口任务而进行设计的,一些简单通用的方法就不需要Map本身实现。
其中比较重要的两个关键字分别是transient和volatile,源码如下:
关于关键词解释:
transient是变量修饰符,表明该字段不是对象持久状态的一部分,储存的时候不用储存,比如序列化这个对象时,该字段是不会储存的。
volatile也是变量修饰符,只能用来修饰变量。volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
在此解释一下Java的内存机制:
Java使用一个主内存来保存变量当前值,而每个线程则有其独立的工作内存。线程访问变量的时候会将变量的值拷贝到自己的工作内存中,这样,当线程对自己工作内存中的变量进行操作之后,就造成了工作内存中的变量拷贝的值与主内存中的变量值不同。
Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。
这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
如何获取最佳Map性能的简单方式,将所有Map变量声明为Map,而不是具体实现,参考实例:
Map cMap = new HashMap(); // 好
HashMap cMap = new HashMap(); // 差
这样可以只更改一行代码即可非常轻松的替换任何特定Map实例。
由于HashMap在java开发中占有比较重要的地位,本文重点分析一下hashmap的重要属性和优化。
HashMap重要属性
HashMap优化
如果哈希映射的内部数组只包含一个元素,则所有项将映射到此数组位置,从而构成一个较长的链接列表。由于我们的更新和访问使用了对链接列表的线性搜索,而这要比Map中的每个数组索引只包含一个对象的情形要慢得多,因此这样做的效率很低。访问或更新链接列表的时间与列表的大小线性相关,而使用哈希函数问或更新数组中的单个元素则与数组大小无关 — 就渐进性质(Big-O 表示法)而言,前者为 O(n),而后者为 O(1)。因此使用一个较大的数组而不是让太多的项聚集在太少的数组位置中是有意义的。
调整Map实现的大小
在哈希术语中,内部数组中的每个位置称作“存储桶”(bucket),而可用的存储桶数(即内部数组的大小)称作容量(capacity)。为使Map对象有效地处理任意数目的项,Map实现可以调整自身的大小。但调整大小的开销很大。调整大小需要将所有元素重新插入到新数组中,这是因为不同的数组大小意味着对象现在映射到不同的索引值。先前冲突的键可能不再冲突,而先前不冲突的其他键现在可能冲突。这显然表明,如果将 Map 调整得足够大,则可以减少甚至不再需要重新调整大小,这很有可能显著提高速度。
看完上述内容,你们掌握Java中如何使用Map集合类的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。