java 原生 map 解析。
HashMapTreeMapLinkedHashMapHashtable
环境说明
java 14
通用用法
| 方法名称 | 说明 |
|---|---|
| clear() | 从 Map 中删除所有映射 |
| remove(Object key) | 从 Map 中删除键和关联的值 |
| put(Object key, Object value) | 将指定值与指定键相关联 |
| putAll(Map t) | 将指定 Map 中的所有映射复制到此 map |
| entrySet() | 返回 Map 中所包含映射的 Set 视图。 Set 中的每个元素都是一个 Map.Entry 对象, 可以使用 getKey() 和 getValue() 方法(还有一个 setValue() 方法)访问后者的键元素和值元素 |
| keySet() | 返回 Map 中所包含键的 Set 视图。删除 Set 中的元素还将删除 Map 中相应的映射(键和值) |
| values() | 返回 map 中所包含值的 Collection 视图。 删除 Collection 中的元素还将删除 Map 中相应的映射(键和值) |
| get(Object key) | 返回与指定键关联的值 |
| getOrDefault(Object key,String defaultValue) | 返回指定键,如果没有返回预定值 |
| containsKey(Object key) | 如果 Map 包含指定键的映射,则返回 true |
| containsValue(Object value) | 如果此 Map 将一个或多个键映射到指定值,则返回 true |
| isEmpty() | 如果 Map 不包含键-值映射,则返回 true |
| size() | 返回 Map 中的键-值映射的数目 |
各类型区别
HashMap- 最常用的
Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为Null(多条会覆盖);允许多条记录的值为Null。非同步的。
- 最常用的
TreeMap- 能够把它保存的记录根据键(
key)排序,默认是按升序排序,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。TreeMap不允许key的值为null。非同步的。
- 能够把它保存的记录根据键(
Hashtable- 与
HashMap类似,不同的是:key和value的值均不允许为null;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢。
- 与
LinkedHashMap- 保存了记录的插入顺序,在用
Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.在遍历的时候会比HashMap慢。key和value均允许为空,非同步的。
- 保存了记录的插入顺序,在用
四种常用Map插入与读取性能比较
网上数据
| 插入10次平均(ms) | 插入10次平均(ms) | 插入10次平均(ms) | 读取10次平均(ms) | 读取10次平均(ms) | 读取10次平均(ms) | |
|---|---|---|---|---|---|---|
| 1W | 10W | 100W | 1W | 10W | 100W | |
| HashMap | 56 | 261 | 3030 | 2 | 21 | 220 |
| LinkedHashMap | 25 | 229 | 3069 | 2 | 20 | 216 |
| TreeMap | 29 | 295 | 4117 | 5 | 103 | 1446 |
| Hashtable | 24 | 234 | 3275 | 2 | 22 | 259 |
初始化
- 常态方法
- 匿名内部类方法
collections实现以及其他- 使用
guava
常态方法
1 | Map<String, String> m = new LinkedHashMap<>(); |
匿名内部类
1 | Map<String, String> m = new LinkedHashMap<>(){{ |
我们必须避免使用这种初始化技术,因为它在每次使用时都会创建一个匿名的额外类,并且还包含对封闭对象的隐藏引用,这可能会导致内存泄漏问题。
collections 实现
单例 Map
1 | Map<String,String> m = Collections.singletonMap("1","test1"); |
这里的 map 是不可变的,如果我们尝试添加更多条目,它将抛出
java.lang.UnsupportedOperationExceptionCollections.emptyMap()创建一个不变的空地图 :
1 | Map<String, String> emptyMap = Collections.emptyMap(); |
java 8
java 8 的实现方式会造成巨大开销,如使用 Streams 进行此类初始化。
感兴趣的可以看下面的文章
java 9
Map.of()
Java 9 在 Map 界面中附带了各种工厂方法 ,从而简化了不可变 map 的创建和初始化。
1 | Map<String, String> emptyMap = Map.of(); |
ps:
- 此方法最多仅支持10个键值对
map都是不可变的
Map.ofEntries()
它与 Map.of()类似, 但对键值对的数量没有限制:
1 | Map<String, String> map = Map.ofEntries( |
请注意,工厂方法会生成不可变的映射,因此任何突变都将导致 UnsupportedOperationException。
而且,它们不允许空键和重复键。
现在,如果在初始化后需要可变的或正在增长的地图,则可以创建Map接口的任何实现, 并将这些不可变的地图传递给构造函数:
1 | Map<String, String> map = new HashMap<String, String> ( |
使用guava
现在,当我们研究了使用核心Java的方式时,让我们继续使用Guava库初始化地图:
1 | Map<String, String> articles |
这将创建一个不变的 map,
创建一个可变的 map:
1 | Map<String, String> articles |
[ImmutableMap.of()] 方法 还具有重载版本,最多可以包含5对键值参数。这是带有两个参数对的示例:
ImmutableMap.of("key1", "value1", "key2", "value2");超过5个 key value时
1 | Map<String, String> test = ImmutableMap.<String, String>builder() |
遍历
增强for循环遍历
使用keySet()遍历
1 | for (String key : map.keySet()) { |
使用entrySet()遍历
1 | for (Map.Entry<String, String> entry : map.entrySet()) { |
迭代器遍历
使用keySet()遍历
1 | Iterator<String> iterator = map.keySet().iterator(); |
使用entrySet()遍历
1 | Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator(); |
有人专门比较过他们的性能
增强for循环使用方便,但性能较差,不适合处理超大量级的数据- 迭代器的遍历速度要比
增强for循环快很多,是增强for循环的2倍左右 - 使用
entrySet遍历的速度要比keySet快很多,是keySet的1.5倍左右
Map 排序
HashMap、Hashtable、LinkedHashMap排序
ps:TreeMap也可以使用此方法进行排序,但是更推荐下面的方法。
1 | Map<String, String> map = new HashMap<String, String>(); |
TreeMap排序
TreeMap默认按key进行升序排序,如果想改变默认的顺序,可以使用比较器:
1 | Map<String, String> map = new TreeMap<String, String>(new Comparator<String>() { |
按value排序(通用)
1 | Map<String, String> map = new TreeMap<String, String>(); |