Map

懵懂的女人 提交于 2020-02-26 23:19:34

对于MAP的理解

映射表的基本思想是:他维护的是键值对的关联,因此可以使用键查找值。标准java类库中的map的几种基本实现包括:HashMap、TreeMap、LinkedHashMap、WeakHashMap、ConcurrentHash、IdentityHashMap。这些都有相同的基本接口Map,但是行为特性不同,表现在效率、键值对的保存及呈现次序、对象的保存周期、映射表如何在多线程程序中工作和判断键等价的策略方面。
关联数组中的基本方法是put()和get(),toString()方法被覆盖为可以打印键值对。使用get()方法,需要传递想要查询的key,后将其关联的值作为结果返回,或返回null。

性能

HashMap使用特殊的值(散列码),取代对键的缓慢搜索,散列码是相对唯一的,用以代表对象的int值,他是通过将该对象的某些信息进行转换生成的。hashCode()是根类Object中的方法,因此所有java对象都能产生散列码。HashMap使用对象的hashCode()进行快速查询。
HashMap:map基于散列表的实现 ,插入和查询“键值对”的开销是固定的,可以通过构造器设置容量和负载因子来调整容器的性能。
LinkedHashMap:类似于HashMap,在迭代遍历它时,取得键值对的顺序是其插入次序,或者是最近最少使用次序,比HashMap慢,在迭代访问时更快,因为它使用链表维护内部次序。
TreeMap:基于红黑树实现,在查看“键”或“键值对”时,被排序,特点在于所得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,可以返回一个子树。
WeakHashMap:弱键映射,允许释放映射所指向的对象,为了解决某类特殊问题而设计的,如果映射之外没有引用指向某个键,则该键可被垃圾收集器收集。
ConcurrentHashMap:一种线程安全的Map,不涉及同步加锁。
IdentityHashMap:使用==代替equals()对键进行比较的散列映射。
对Map中使用的键的要求与set中的元素要求一样,任何键都必须具有一个equals()方法,如果键被使用于TreeMap,则必须实现Comparable。
以下实例通过Map接口可用的操作:
在这里插入图片描述
在这里插入图片描述
printKeys()展示如何生成Map的Collection视图,keySet()返回由Map的键组成的Set
,该方法会产生一个包含Map中所有值的Collection。

SortedMap

使用SortedMap可以确保键处在排序状态,使得它具有额外的功能。
Comparator comparator():返回当前Map使用的Comparator;或返回null,表示以自然方式排序。T firstKey()返回Map中的第一个键,T lastKey()返回Map中的最后一个键。sortedMap subMap(fromKey,toKey)生成此Map的子集,范围由fromKey(包含)到toKey(不包含)的键确定。SortedMap headMap(toKey)生成此Map的子集,由键小于toKey的所有键值对组成,SortedMap tailMap(fromKey)生成此Map的子集,由键大于或等于fromKey的所有键值对组成。
下面实例演示TreeMap新增功能:
在这里插入图片描述
在这里插入图片描述键值对是按照键的次序排列的。

LinkedHashMap

LinkedHashMap散裂化所有元素,但是在遍历键值对时,却又以元素的插入顺序返回键值对。可以在构造器中设定LinkedHashMap使之采用访问的最近最少使用算法,于是没有被访问过的元素就会出现在队列的前面。对于需要定期清理元素以节省空间的程序来说,该功能容易实现。
在这里插入图片描述
在这里插入图片描述

散列与散列码

在这里插入图片描述
在这里插入图片描述
HashMap使用equals()判断当前的键是否与表中存在的键相同。正确的equals()方法需满足以下五个条件:
1.自反性:对任意x、x.equals(x)一定返回true。
2.对称性:对任意x和y,如果y.equals(x)返回true,则x.equals(y)也返回true。
3.传递性:对任意x、y、z,如果有x.equals(z)返回true,则x.equals(z)一定返回true。
4.一致性:对任意x和y,如果对象中用于等价比较的信息没有改变,则无论调用x.equals(y)多少次,返回的结果应该保持一致,要么一直是true,要么一直是false。
5.对任意不是null的x,x.equals(null)一定返回false。
默认的Object.equals()只是比较对象的地址,而不是具体数值。如果要使用自己的类作为HashMap的键,必须同时重载hashCode()和equals()
在这里插入图片描述
在这里插入图片描述
Groundhog2.hashCode()返回Groundhog的标识数字作为散列码。上例中需要确保不同的Groundhog有不同的编号,hashCode()不需要总是能够返回唯一的标识码,但是equals()需严格判断两个对象是否相同。
尽管equals()方法只是检测其参数是否是Groundhog2的实例,但是instanceof检查了此对象是否为null,当instanceof左边的参数为null,会返回false。如果equals()的参数不为null且类型正确则基于每个对象中实际的number数值进行比较。

理解hashCode()

使用散列的目的在于:想要使用一个对象来查找另一个对象。使用TreeMap或子集实现的Map也可以达到刺目的。与散列实现相反,下面的例子使用一对ArrayLists实现一个Map。
在这里插入图片描述
在这里插入图片描述put()方法只是将键与值放入相应的ArrayList,必须返回就得键或者在没有任何九剑的情况下返回null。
get()会在键不在SlowMap中的时候产生null。如果键存在则将被用来查找表示他在keys列表中的位置的数值型索引,并且该数字被用作索引来产生于values列表相关的值。在get()中key的类型是Object,而不是参数化类型K。Map.entrySet()方法必须产生一个Map.Entry的对象集。但是Map.Entry是一个接口,用来描述依赖与实现的结构,因此如果想要创建自己的Map类型就必须同时定义Map.Entry的实现。
在这里插入图片描述在这里插入图片描述这个被称为MapEntry的类可以保存和读取键和值,在entrySet()中用来产生键值对Set。entrySet()使用了HashSet保存键值对并且MapEntry采用了只使用key的hashCode()方式。

为速度而散列

散列的价值在于速度,散列使得查询得以快速的进行。因为瓶颈位于键的查询速度,因此解决方法是保持键的排序状态,然后使用Collection.binarySearch()进行查询。
在散列中将键保存在某处以便能够快速找到。使用数组表示键的信息。数组并不保存键的本身而是通过键生成一个数字将其作为数组的下标,该数字是散列码,由定义在Object中且可能由类覆盖的hashCode()方法生成。
为解决数组容量被固定的问题,不同的键可以产生相同的下标,因此可能会产生冲突。
查询一个值的过程就是首先计算散列码,然后使用散列码查询数组。如果能够保证没有冲突则就有了一个完美的散列函数。一般冲突由外部连接处理,数组并不直接保存值,而是保存值的list。然后对list中的值使用equals()方法进行线性查询。这部分查询比较慢,但是散列函数好的话,数组的每个位置就只有较少的值。因此,不是查询整个list,而是快速的跳到数组的某个位置,只对很少元素进行比较。这便是HashMap如此快的原因。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!