java
原生 map
解析。
HashMap
TreeMap
LinkedHashMap
Hashtable
环境说明
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.UnsupportedOperationException
Collections.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>(); |