网站首页 > java教程 正文
Java 17 Map 接口知识点
思维导图
Map 集合可以认为是一个包含键值对的数据集合。也可以称为键值对。并且对于键(key)而言,是不允许重复的。并且每个键只能映射到一个值上。对于 key 相等时, 就认为该键值对是相同的数据。对于 Java 平台来说包含了三个通用的实现。 分别是:HashMap、LinkedHashMap 以及 TreeMap。
对于三个通用实现的行为和性能基本上是相似的。 在老的版本里面还有 Dictionary<K,V>, C# 使用这种方式, 但是 Java 中已经不推荐继承这个类去扩展了,而是实现 Map 接口。但依然还会有部分以后的类使用了该接口。
Map的定义:
public interface Map<K, V>
对于 Map 集合, 同样会有增删改查基础的功能。
- put(K key, V value); 新增数据,将指定的值和键进行关联。
- remove(Object key); 从映射中删除键关联的值(当然这个键存在的话)
- entrySet(); 返回此映射中包含的映射的 Set 视图。
- replace(K key, V value); 仅当指定键当前映射到某个值时,才替换该项的条目。
- size(); 返回此映射中键值映射的数目。
这些都是常用的方法, 更多的内容可以参考API。
API地址: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Map.html
AbstractMap
对于 Map 接口, 使用的时候, 必须进行实现, 不实现就无法进行使用。 并且对于 Java SDK 默认有通用的实现。 实现之前为了使用多种不同的实现, 会进行抽象封装, 所以就有了抽象类 AbstractMap。对于该接口和 AbstractSet 和 AbstractList 一样, 都是为了减少实现上层接口的工作量。
抽象类的定义:
public abstract class AbstractMap<K,V> implements Map<K,V>
实现的相关方法:
抽象类只是实现了通用的一些变化不大的实现。 对于实际的 put 方法, 以及如何遍历元素都没有具体的实现。
只是直接返回了 UnsupportedOperationException (不支持的操作异常)。
HashMap
上面说了抽象类相关的定义, 接下来看看第一个常用的实现。 HashMap<K,V>。他主要是基于哈希表进行实现。 它的实现提供了全部的 Map 接口可选的操作。 对于哈希表(Hash table)也叫作散列表,是根据键和值进行访问的数据结构,只需要输入对应的 key,就可以查找到对应的值 value。
类实现的定义:
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
从实现的数据结构上来看, HashMap 是用到了数组、链表、红黑树(从 JDK8 加入)。
对于 HashMap 而言是非同步的, 也就是说在操作中没有锁的保护, 将会出现数据不一致的情况出现。在单线程中可以放心使用。
举个例子, 张三这个用户, 对系统有三个权限, 浏览, 修改和删除权限。 这个时候, 我们可以模拟一下数据的操作。
先创建一个权限对象。
class Auth {
private String id;
private String name;
public Auth(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Auth [id=" + id + ", name=" + name + "]";
}
}
为了调试方便, 重写了 toString() 方法。
这只是简单的使用方式, 可以快速的定位是否包含某个权限, 可能会进行更改, 保存的键是权限的主键。
在代码的测试后面加上下面的代码
System.out.println("删除 编码3 的删除权限");
map.remove("3");
mapUser.put("zhangsan", map);
authDel = mapAuth.get("3");
if(authDel != null){
System.out.println("包含删除的权限。");
}else{
System.out.println("不包含删除的权限。");
}
执行代码,查看。
对于 HashMap 排序是随机的
对于 HashMap 是无序的, 测试案例。
测试代码如下:
执行运行查看效果:
LinkedHashMap
如果使用 HashMap 的同时, 还想保持插入的顺序并能更好的排序怎么办呢? 可以使用 LinkedHashMap 实现。
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
该类是基于哈希表和链表的实现,具有可预测的迭代顺序。对于该类的实现和 HashMap 的区别在于它内置了一个双重链表列表,此链表定义了迭代顺序。通常是将键插入的顺序(insert-order)。
举例如下:
所以对于业务中需要使用 Map 键值对并且想要保证数据的顺序的就可以使用该类。
当然对于键来说, 可以通过方法直接知道是否包含该值和该键。 演示如下:
除了这些常用的使用方式, 还有是否为空, 以及集合的大小。
map.size(); // 集合的大小
map.isEmpty(); // 如果集合没有对应的键值对数据就返回 true
WeakHashMap
对于 WeakHashMap 是基于哈希表实现的, 但是相对于上面两种 Map 实现, 它是弱键(weak keys)的实现。弱键又被称为弱引用(Weak Reference) 也就是在使用过程中也会有可能被 GC 自动回收清理的。对于 GC 的自动回收的依据是:是否有有效的引用指向该对象。这里有效的引用不包含弱引用。 所以在 GC 回收垃圾的时候, 还是有可能会对弱引用的对象收回的。
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>
对于它的使用场景, 经常用于缓存中, 因为缓存不需要必须保证数据一直存在, 即使没有命中, 还有处理机制重新赋值。这样也避免了长时间不用一直占用资源的情况。
这里对于使用上没有任何的区别。 注意使用场景就好了。
给一个实际中的项目例子: tomcat 中的 ConcurrentCache 就使用了 WeakHashMap
代码来源:https://github.com/apache/tomcat/blob/main/java/org/apache/el/util/ConcurrentCache.java
IdentityHashMap
该类的定义
public class IdentityHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, java.io.Serializable, Cloneable
虽然此类使用哈希表实现了 Map 接口, 虽然此类实现了 Map 接口, 但是它违反了 Map 的通用约定,该约定要求在比较对象时使用equals方法。此类设计为仅在需要引用相等语义的极少数情况下使用。 不再过多的演示。
TreeMap
基于红黑树的 NavigableMap 接口实现的。和上一章说过的 Set 一直, 可以通过 key 进行排序。 以及可以通过 Comparator 接口进行排序。 再次重申数据结构的重要性。 花点精力要好好的吃透数据结构, 能给你技术有质的飞跃。从不懂到了解, 从了解到精通, 从精通到直觉, 内心用就应该这样用, 就给你一开始学习编程, 就很难理解定义变量怎么来定义, 突然有一天顿悟了, 字符串就是要定义字符串。 数值就是要定义数值。 希望我们下一次的顿悟来的更早一点。 继续,扯得有点远了。
EnumMap
用于枚举类型的专用 Map 实现。key 继承自 Enum。对于枚举中的键值对中的键都必须来自创建对象时的显式或隐式指定的单个枚举类型, 对于枚举类型的键值对内部是用数组实现,对于这种实现非常的紧凑和高效。
对于它的实现, key 键是不允许为空的。 插入 null 时, 会有 NullPointerException 异常。但是在测试的时候判断是否为 null 键,或者删除 null 键不会提示异常。
EnumMap 的定义:
public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
implements java.io.Serializable, Cloneable
举个简单的例子:
这里需要注意的是, 该实现的构造函数没有默认的构造方法, 基于 Java 自定义构造方法, 不会生成默认的构造方法的规则, 所以直接使用空的构造方法是不行的。
所以下面的将会提示错误。
对于 Map 的遍历
对于 Map 遍历一般情况下,有以下三种常用的方法。
第一种方式 keySet()
Set<String> keySet = map.keySet();
for (String key : keySet) {
// 循环所有key,通过get方法打印所有value
System.out.print("key: " + key + ", value: " + map.get(key) + "\n");
}
使用 keySet() 方法获得所有 Map 中的 key。并使用 get(key) 获得对应的值。
第二种方式 entrySet
entrySet 方法的定义
Set<Map.Entry<K, V>> entrySet();
其中 Map.Entry<K, V> 中的 Entry 是一个 Map 中定义的内部接口 interface Entry<K, V>。在其实现类中实现了该接口 static class Node<K,V> implements Map.Entry<K,V>。
Set<Map.Entry<String, String>> entrySet = map.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
System.out.println("key: " + entry.getKey() + ", value: " + entry.getValue());
}
当然也可以使用迭代器进行实现, 举例代码如下:
Iterator<Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Entry<String, String> entry = it.next();
System.out.println("key: " + entry.getKey() + ", value: " + entry.getValue());
}
第三种方式 values
该方式主要是为了遍历对应的值, 而不需要键的场景上使用。
Collection<String> values = map.values();
for (String str : values) {
System.out.println("value: " + str);
}
如果想同事操作多个集合或者针对集合进行条件的汇总, 集合的合并可以使用封装类 Collectors。 这里不针对该类进行详细的说明。 后续单独开一篇。 今天这篇内容有点超长。 如果你能读到这, Java 对你来说就太容易了。
Map 接口的知识点先到这里吧。 后续更多原理性的东西,数据结构篇会讲到。 码字不易。
点赞, 关注, 收藏。 十分感谢!
编程是一种终身学习持续不断的事,开始就要做好准备,当然其他行业也是一样。躺平也要休息的有意义!。
猜你喜欢
- 2024-10-26 Java8 List转Map,我卡壳了......
- 2024-10-26 HashMap 的 7 种遍历方式与性能分析!(强烈推荐)
- 2024-10-26 Java集合-- Map(Java集合类)
- 2024-10-26 js 函数式编程:不要再使用 for 循环啦,试试 map 吧
- 2024-10-26 大厂Java二面:Spring循环依赖,烂大街的问题这么答面试官才满意
- 2024-10-26 JAVA集合之 MAP和HASHMAP(java中map和hashmap)
- 2024-10-26 双列集合Map不再难懂:轻松掌握这些知识点!
- 2024-10-26 用到停不下来,Java 8 新特性:foreach 和 stream
- 2024-10-26 Go语言开发者必知必会的Map优化技巧
- 2024-10-26 计算机程序员的入门实践-Map常用的遍历方式(七)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)