前面总结的List,Set,Queue都是Collection下的实现类,本篇博客总结的是与Collection并列的集合,Map.
Map是key-value键值对,key不允许重复,value可以。
Map的常用实现类主要有:HashMap,TreeMap,LinkedHashMap,HashTable.
一. HashMap和HashTable
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
HashMap和Hashtable都是Map接口的经典实现类,它们之间的关系完全类似于之前介绍的ArrayList和Vector的关系。
(一)区别:
1.Hashtable是一个线程安全的Map实现,但HashMap是线程不安全的实现,所以HashMap比Hashtable的性能好一些;但如果有多个线程访问同一个Map对象时,是盗用Hashtable实现类会更好。
2.Hashtable不允许使用null作为key和value,如果试图把null值放进Hashtable中,将会引发NullPointerException异常;但是HashMap可以使用null作为key或value。
(二)判断key与value相等的标准:
key判断相等的标准:类似于HashSet,HashMap与Hashtable判断两个key相等的标准是:两个key通过equals()方法比较返回true,两个key的hashCode值也相等,则认为两个key是相等的。
value判断相等的标准:只要两个对象通过equals()方法比较返回true即可。
(三)HashMap的构造函数:
// 默认构造函数。
HashMap()
// 指定“容量大小”的构造函数
HashMap(int capacity)
// 指定“容量大小”和“加载因子”的构造函数
HashMap(int capacity, float loadFactor)
// 包含“子Map”的构造函数
HashMap(Map<? extends K, ? extends V> map)
构造函数中,两个重要的元素为容量大小和加载因子。
容量(capacity)是哈希表的容量,初始容量是哈希表在创建时的容量(即DEFAULT_INITIAL_CAPACITY = 1 6)。
加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 resize操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。
通常,默认加载因子是 0.75(即DEFAULT_LOAD_FACTOR = 0.75f), 这是在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。在设置容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少 resize操作次数。如果容量大于最大条目数除以加载因子,则不会发生 rehash 操作。
(四)HashMap的遍历方式(此处省略构造HashMap代码):
1.遍历HashMap的键值对
//通过 Map.entrySet() 得到 Map 的 Entry集合,然后遍历
Set<Map.Entry<String, Object>> entrys = hashMap.entrySet();
for(Map.Entry<String, Object> entry: entrys){
String key = entry.getKey();
Object value2 = entry.getValue();
System.out.println(key+"="+value2);
}
2.遍历HashMap的键
//通过 map.keySet() 得到 Map 的key 的集合,然后 通过 get(key) 得到 Value
Set<String> set = hashMap.keySet();
for(String str : set){
Object obj = hashMap.get(str);
//System.out.println(str+"="+obj);
}
3.遍历HashMap的值
//通过 map.values() 方法得到 Map 中的 value 集合
Collection<Object> value = hashMap.values();
for(Object obj : value){
//System.out.println(obj);
}
二. LinkedHashMap
HashSet有一个LinkedHashSet子类,HashMap也有一个LinkedHashMap子类;LinkedHashMap使用双向链表来维护key-value对的次序。
LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap的性能;但是因为它以链表来维护内部顺序,所以在迭代访问Map里的全部元素时有较好的性能。迭代输出LinkedHashMap的元素时,将会按照添加key-value对的顺序输出。
本质上来讲,LinkedHashMap=散列表+循环双向链表
三. TreeMap
TreeMap是SortedMap接口的实现类。TreeMap 是一个有序的key-value集合,它是通过红黑树实现的,每个key-value对即作为红黑树的一个节点。
TreeMap排序方式和TreeSet一样,包括自然排序和定制排序。
TreeMap的本质:红黑树。关于红黑树的介绍,曾看到过一篇文章:
https://www.sohu.com/a/201923614_466939 《漫画算法:什么是红黑树》
四. 适用场景
一般的应用场景,尽可能多考虑使用HashMap,因为其为快速查询设计的。
如果需要特定的排序时,考虑使用TreeMap。
如果仅仅需要插入的顺序时,考虑使用LinkedHashMap
五. Map在项目中的应用
Map在项目中也用的挺多,在定义方法的返回类型时,一般会根据返回对象确定是返回实体还是使用Map构造结果集。在项目中,也经常会使用List’<’Map’<’String,Object>>作为结果集返回。下面给出一个简单的代码应用,查询结果集为List’<’Map’<’String,Object>>,最后返回的结果是按照某一key值进行排序的:
List<Map<String, Object>> resultMapList = companyService.queryNearlyCompany(position, area_id, distance, platformId);
ReturnMsg msg = ReturnMsg.getSuccessMsg();
if (resultMapList != null && !resultMapList.isEmpty()) {
logger.info("排序前" + resultMapList);
CaluateDistanceUtil.listSort(resultMapList);
logger.info("排序后" + resultMapList);
msg.getData().put("companies", resultMapList);
} else {
msg.getData().put("companies", Collections.EMPTY_LIST);
}
//将查询到的集合按照距离从近到远进行排序
public static void listSort(List<Map<String, Object>> resultList) {
Collections.sort(resultList, new Comparator<Map<String, Object>>() {
@Override
public int compare(Map<String, Object> o1, Map<String, Object> o2) {
double distance1 = Double.parseDouble(MapUtils.getString(o1, "distance"));
double distance2 = Double.parseDouble(MapUtils.getString(o2, "distance"));
double p = distance1 - distance2;
if (p > 0) {
return 1;
} else if (p == 0) {
return 0;
} else {
return -1;
}
}
});
}