Map 继承关系 主要实现类 Map的遍历 总结
Map
Map 是Java.util包中的另一个接口,和collection接口没有关系,是相互独立的,但是隶属于集合类的一部分,map包含了(key-value)对,map不能
包含重复的key,但是可以包含相同的value;一组成对的“键值对”对象,允许你使用键来查找值,ArrayList允许你使用数字来查找值,某种意义上,
将数字与对象关联在一起,映射表允许我们使用另一个对象来查找某个对象,他被称为“关联数组”,因为它将某些对象关联在一起,或者称为“字典”,唯
一需要指定所使用的精确类型的地方就是在创建的时候;
一个映射不能包含重复的键;每个键最多只能映射到一个值。Map 接口提供三种collection 视图,允许以键集(keySet())、值集(values())或键-值
映射关系集(entrySet())的形式查看某个映射的内容( 即获取键值对的内容 )。映射顺序定义为迭代器在映射的 collection 视图上返回其元素的顺序,
即可以映射得到键、值和键-值的Set集合,元素的顺序是由得到的Set集合所决定的。某些映射实现可明确保证其顺序,如 TreeMap 类;另一些映射实现
则不保证顺序,如 HashMap 类。
继承关系
继承图参考自博客
主要实现类
Map有主要实现类为HashMap、Hashtable、LinkedHashMap和TreeMap;
HashMap
HashMap:最常用的map,根据键的hashcode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是随机的,因为
键对象不可以重复,所以hashmap最多只允许一条记录的键为null,允许多条记录的值为null,是非同步的;hashmap的工作原理:hashmap是基于
hashing的原理,使用put(key,value)存储对象到hashmap中,使用get(key)获取对象,当我们给put()方法传递键和值时,先对键调用
hashCode()方法,返回hashCode用于找到bucket位置来存储Entry对象,HashMap是在bucket中存储键对象和值对象,作为Map.Entry。
hashCode和equals方法
HashMap其中涉及到Object类的两个方法,hashCode和equals,equals方法实现对象上差别可能性最大的相等关系,即,对于任何非空引用值x和y,
当且仅当x和y引用同一个对象时,此方法才返回true,当此方法被重写时,通常有必要重写hashCode方法,以维护hashCode方法的常规协定,该协定
声明相等对象必须具有相等的哈希码;
关于hashCode方法,是为哈希家族的集合类框架(HashMap\HashSet\HashTable)提供服务的,其常规协定:在 Java 应用程序执行期间,在同一对象上
多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用
程序的另一次执行,该整数无需保持一致。 如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都
必须生成相同的整数结果。
以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会
生成不同的整数结果。但是,为不相等的对象生成不同整数结果可以提高哈希表的性能。
hashmap的类结构:
java.lang.Object->java.util.AbstractMap<K,V>->java.util.HashMap<k,v>;
直接已知子类:LinkedHashMap,PrinterStateReasons;
HashMap的性能:
hashmap有两个参数影响性能,初始容量和加载因子
默认初始容量是16,加载因子0.75,容量就是哈希表中桶entry数组的数量,初始容量只是哈希表在创建时的容量,加载因子是哈此表在其容量自动增加
之前可以打扫多满的一种尺度;当哈希表中的条目数量超过了加载因子与当前容量的乘积时,通过调用rehash方法将容量翻倍;
rehashing:默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap
大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的
bucket位置
Hashtable
Hashtable:hashtable与hashmap类似,但是是线程安全,支持线程的同步,即任意时刻只有一个线程能写hashtable,因此导致hashtable在写入时
会比较慢,它继承自Dictionary类,不同的是它不允许记录的键或者值为null,同时效率比较低;
LinkedHashMap
linkedhashmap保存记录了插入顺序,在用Iterator遍历linkedhashmap时,得到的记录肯定是先插入的,在遍历的时候会比hashmap慢,
有hashmap的全部特性;
TreeMap
此部分引用了该博客 TreeMap:TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按照键值的升序(自然顺序),也可以指定排序的比较器,当用Iterator遍历treemap的时候,得到的记录是排过序的,不允许key值为空,非同步; 基于红黑树(Red-Black tree)的NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。 键值对是红黑树结构,可以保证键的排序和唯一性 此实现不是同步的 须为TreeMap提供排序方案
①根据其键的自然顺序进行排序(自定义对象须实现Comparable接口并重写compare方法) ②根据创建映射时提供的Comparator进行排序,具体取决于使用的构造方法。
分析TreeMap的put方法源码 1、判断Entry是否有元素,没有则new一个,并添加新元素 2、通过自然排序与比较器排序来为TreeMap排序,优先使用Comparator来排序
通过Entry对象来来确保插入元素的唯一性,它建立在compare方法的基础上,此方法返回0时,表示插入的键存在,直接替换旧值并返回。因此在使用TreeMap时,自定义对象须实现Comparable接口并重写compare方法或在创建TreeMap时提供Comparator cmp = cpr.compare(key, t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); 3、在插入元素后重新调整红黑树(即Entry树)
WeakHashMap
以弱键实现的基于哈希表的 Map。在 WeakHashMap 中,当某个键不再正常使用时,将自动移除其条目。更精确地说,对于一个给定的键,其映射的存
在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。丢弃某个键时,其条目从映射中有效地移除,因此,该类的行
为与其他的 Map 实现有所不同。null 值和 null 键都被支持
实现注意事项:
WeakHashMap 中的值对象由普通的强引用保持。因此应该小心谨慎,确保值对象不会直接或间接地强引用其自身的键,因为这会阻止键的丢弃。注意,
引用第一个值对象的键。处理此问题的一种方法是,在插入前将值自身包装在 WeakReferences 中,如:m.put(key, new WeakReference(value)),然后,分别用 get 进行解包。
Map的遍历
方式1、根据键获取值(key -> value)
1.获取所有键的集合
2.遍历键的集合,获取到每一个键
3.根据键找值
KeySet();
将map中所有的键存入到set集合中,因为set具备迭代器,所有可以迭代方式取出所有的键,再根据get()方法获取每一个键对应的值,KeySet():迭代后只能通过get()取key取到的结果会乱序,因为取得数据行主键的时候,使用了HashMap.keySet()方法,这个方法返回的Set结果数据时乱序排放的。 Iterator it = map.keySet().iterator();//获取迭代器 while(it.hasNext()){ Object key = it.next(); System.out.println(map.get(key)); }
方式2、根据键值对对象获取键和值( entrySet -> key,value)
1.获取所有键值对对象的集合
2.遍历键值对对象的集合,获取到每一个键值对对象
3.根据键值对对象找键和值
entrySet():
返回此映射中包含映射关系的Set视图(一个关系就是一个键-值对),就是把(key-value)作为一个整体一对一存放到set集合中;Map.Entry表示映射关系。 entrySet():迭代后可以e.getKey(),e.getValue()两种方法来取到key和value,返回的是Entry接口,用法: Iterator it = map.entrySet().iterator(); while(it.hasNext()){ Entry e = (Entry)it.next(); System.out.println(“k”+e.getKey() +”v” +e.getValue()); } 推荐使用这种,效率高,对于keySet其实是遍历了两次,一次是为了转为iterator,一次是从hashmap中取出key所对于的value,而entrySet只遍历了一次,把key和value放入entry中,所以比第一种快;
总结
关于HashMap还有很多东西,这里只是根据接触历程记录一下,下文为关于hashmap的一些实际应用问题思考。