Map

对于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如此快的原因。
下面的例子是一个散列的Map:
在这里插入图片描述在这里插入图片描述在这里插入图片描述
对于put()方法,hashCode()将针对键而被调用,并且其结果被强制转换为正数。为了使产生的数字适合bucket数组的大小,取模操作符将按照该数组的尺寸取模。如果数组的某个位置是null,表示还没有元素被散列到此。一般过程为查看当前位置的list中是否有相同的元素,如果有,则将旧的值赋予oldValue,然后用新的值取代旧的值。标记found用来跟踪是否找到旧的键值对,如果没有,则将新的对添加到list的末尾。
get()方法计算在buckets数组中的索引与put()方法相同。

覆盖hashCode()

设计HashCode()是最重要的因素为:对于同一个对象调用hashCode()都应该生成同样的值。如果将一个对象用put()添加到HashMap时产生的一个hashCode()值,而使用get()方法取出时却产生另一个hashCode()值,那么无法重新取得对象。因此不应该使用依赖于对象中易变的数据。
此外,不应该使hashCode()依赖于具有唯一性的对象信息,尤其是使用this的值。因为这样做无法生成一个新的键是指与put()中原始的键值对中的键相同。
String有一个特点:如果程序中有多个String对象都包含相同的字符串序列,那么这些String对象都映射到同一块内存区域。如下面一个例子:
在这里插入图片描述因此对于new String(“hello”)生成的两个实例虽然是相互独立的,但是对他们使用hashCode()应该是生成同样的结果。
要想使用hashCode(),必须给予对象的内容生生成散列码。散列码不必是独一无二的,但是通过hashCode()和equals()必须能完全确定对象的身份。好的hashCode()应该产生分布均匀的散列码。
comparTo()方法有一个比较结构,因此会产生一个排序序列,排序序列的规则是首先按照实际类型排序,然后如果有名字则按照name排序,最后按照创建的顺序排序。如下例所示:
在这里插入图片描述
由于所有宠物都有名字,因此首先按照类型排序,然后在同类型中按照名字排序。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值