Map > AbstractMap > HashMap
Map
翻译:
一个键值对的集合。 一个map不能包括重复的key,一个key最多对应一个值。(即通过一个key最多能找到一个值,包括没有值的情况)
这个接口是用来替换的Dictionary
类,因为Dictionary
是一个抽象类,而不是一个接口。
Map提供了三种观察集合的方式,包括:
- a set of keys 键的集合
- collection of values 值的集合
- set of key-value mappings 键值对的集合
遍历map的顺序,定义为,迭代map并返回集合元素的顺序。每次迭代,得到元素的序列。在具体实现上,每次遍历TreeMap
返回的序列顺序是唯一,而HashMap
则不唯一。
在key是可变的变量(即不用final
修饰的变量)时,若是该变量的指向对象改变,Map是没有对该行为负责的。(尽量不要使用final的元素作为key)。
Map中是不允许用自身作为它的一个key;若是把自身作为其中一个value,则在比较对象的是否相等的可靠性也不可保证。
通常的Map的实现都提供两个构造方法,一个无参的,一个Map(Map),接受一个map作为参数来作为初始元素。当然这只是个建议,接口没有强制这个建议 手段,但是JDK的中的实现都遵循了这个建议。
有一些方法是没有具体实现的,所以抛出支持该操作的异常。
一个Map的实现,可能会对KEY和Value的类型做限制。比如允许key-value为空的,或对键的类型做限制。不顾这些限制会可能抛出异常。
很多集合框架,都会要求比较的两个对象是非空类型,这里Map不做要求。
一些操作会有’self-referential’的情形,在遍历这个集合时,无论是直接或间接引用自身
,有可能要特殊处理。
1 | public interface Map<K,V>{ |
AbstractMap
翻译:
这个类提供了对Map
提供了骨架的实现,减轻了子类对Map实现的负担。
要是实现一个不可变的Map,(unmodifiable map),只要继承该类并实现entrySet()
方法,也就没有add
,remove
方法的具体实现,若是调用了直接报异常。
实现一个可变的Map,就要实现add()
, remove()
方法了,并且在它的迭代器要同时实现添加移除的方法。
1 | public abstract class AbstractMap<K,V> implements Map<K,V>{ |
HashMap
翻译:
使用了哈希表来实现了Map
接口。实现了所有可选的方法,允许key和value为空的情况。
HashMap
类似HashTable
,不同之处在于HashMap
不同步的,且允许null的情况。由于使用Hash表实现的方式,所以遍历是出来的元素顺序不保证相同。
HashMap的在对元素get
,put
操作时,提供了常量时的性能(时间复杂度为O(1)),前提是哈希函数能够将元素合理地分散在桶子里。(愿译:assuming the
has function disperses the elements properly among buckets.) 遍历集合视图(即遍历所有元素)需要时间复杂度n(n=桶子的数量+
所有链表的元素size之和)。所以在设置初始容量(initial capacity)或是加载因子太低(the load factory),否则遍历性能会很低。
HashMap有两个参数会决定它的性能,初始容量和加载因子。初始容量就是在在哈希表创建时,桶子的数量,加载因子是表示哈希表多满时 (即装元素的桶子数目/所有桶子数目),容量会自动增加。当前元素数量超出capacity*factor之后,哈希表需要rehashed(内部数据结构重建),表的桶子数量 大约翻倍。
通常说,加载因子默认为0.75,是在时间与空间较合适的选择。加载因子高了,空间利用高,但是在操作上(put,get)花的时间会变高。若是可以提前估计要存放的 的数量,来设置初始容量和加载因子是最好不过的,要是设置初始容量大于实际存放的最大值,过程中就可以避免rehashed。
要是每个entry的hasCode()
返回同一值,那么出现一种极端情况,相当于将数据存放在一个链表了,查询速度会很低(O(N)。
HashMap不是线程安全的,若是有多个线程同时访问一个Map,若有一个线程对Map有结构修改,必须在外部保证其线程安全。结构性调整是比如添加,删除,仅是修改
key对应的值是不归于的。具体处理,可以
Map m = Collections.syschronizedMap(new HashMap(…));
在迭代器创建后,有结构性修改发生,除了使用迭代器使用自己的remove方法,会抛出ConcurrentModificationException
,即快速失败
fail-fast,在
面对不可靠的数据时,尽量快的抛出异常而不是允许数据不可靠。但是,也不可能根据抛异常来判断是否可靠,这个异常仅应该用来检测bug。
1 | public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>,Clonable,Seriazable{ |
关于普通对象重写的hashcode(), equals()方法的常见问题
重写一个bean类时,使用idea右键generate重写两个方法
1 | public class A { |