吃透Java集合系列八:Map

2024-09-03 01:48
文章标签 java 系列 map 集合 吃透

本文主要是介绍吃透Java集合系列八:Map,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Map接口分析

关于Map接口,JDK是这样描述的:

  • Map是一个有键值对映射的对象,map不能包含相同的key,每一个key至多能映射一个value。
  • Map替代了Dictionary这个类,Dictionary是抽象类而非接口,替代原因:接口总是优于抽象类。
  • Map接口提供了3个集合视图,包括:keys的set集合, values的集合;,key-value的set集合,注意:values集合不是set类型,因为value可相同。
  • Map返回元素的顺序,取决于map对应的某个集合视图迭代器的顺序,一些map的实现,比如TreeMap类,对于map返回元素的顺序有特殊的规定,其它的map实现类,比如HashMap类,就没有特殊的规定。
  • 不要把异变的对象作为Map的key,如果map中的一个key发生了改变,并且影响了equals()方法的使用,那么map并不会提示我们。
  • 所有的通用map实现类都应该提供两个"标准"的构造器函数,一个是无参且返回类型为void的函数,另一个就是仅含有一个参数且类型为map类型的的构造方法,这个方法会使用其参数构造一个新的map,且新的map和参数map有相同的key和value,但是,map接口这里没办法强制执行这一建议(因为接口里面不能包含构造器函数),不过JDK里面所有通用的map实现类都是符合这一点要求的。
  • 一些map的实现类在key和value的取值上面会有一些规定,比如,一些map实现类不允许key或者value为null。
  • map接口是java集合框架中的一个成员。
  • 集合框架接口定义了很多种和equals()方法相关的实现,比如,containsKey()方法,当前仅当map包含了键k的定义是:key = = null ? k == null : key.equals(k),这一规范的写法,不能被理解为为:如果调用方法使用的是一个非null参数的话,然后只是再调用key.equals(k)方法就可以了,具体实现可以通过避免调用equals()方法来实现优化,比如,可以先比较两个key的哈希值,(哈希值保证了,如果两个对象的哈希值都不相同,那么这两个对象肯定不会相同),更一般的情况是,大量集合框架接口的实现类可以充分利用底层对象(Object)的方法的优势,只要实现者认为他们这么做是合理的。
public interface Map<K,V> {//增/*** put方法是将指定的key-value存储到map里面的操作.如果map之前包含了一个此key对应的映射,* 那么此key对应的旧value值会被新的value值替换.*/V put(K key, V value);/*** putAll方法是将一个指定map的映射拷贝到当前map.这一操作类似于将指定map的key-value对通过put方法一个个拷贝过来。* 在拷贝过程中,如果指定的这个map被更改了,那么这时候会出现什么情况,并不清楚。*/void putAll(Map<? extends K, ? extends V> m);//删/*** remove方法用于移除map中已有的某个key.更一般的讲,如果map包含了一个满足条件key==null ?  k==null : key.equals(k)的映射,* 这一映射就会被移除.(map最多包含一个这样的映射)* 本方法会返回移除的key对应的value值,如果map这个key没有对应的value值,则返回null。* 如果map允许null值,那么返回null值并不一定表明map不包含对应key的映射值;因为这也可能是key本身对应的value值就是null.*/V remove(Object key);/*** 移除map中所有的映射*/void clear();//查/*** 返回map中key-value映射的个数.如果map包含的key-value个数超过了Integer.MAX_VALUE这个数* 则返回Integer.MAX_VALUE.*/int size();/*** 如果map没有存储任何key-value,则返回true*/boolean isEmpty();/*** 如果map存储了指定的key,则返回true.更一般的情况是,当且仅当map包含了一个key的映射* 映射情况是:key==null ? k==null : key.equals(k),此时返回true.*/boolean containsKey(Object key);/*** 如果map中至少有一个key能映射到指定的value,那么就返回true.更一般的情况是,* 当且仅当value==null ? v==null : value.equals(v)条件成立,才返回true.* 在所有map接口的实现类中,这一操作都需要map大小的线性时间来完成.*/boolean containsValue(Object value);/*** 返回指定key映射的value.如果map没有指定的key,则返回null.*/V get(Object key);//三个返回key,value,key-value的集合/*** 此方法:返回map包含所有的key的一个set集合视图*/Set<K> keySet();/*** values方法返回map内存储的所有值的集合(毕竟值集合中,值可以有重复的,所以此方法和上面的返回的* key集合的结果类型不一样,因为key肯定都是不同的).*/Collection<V> values();/*** 此方法返回map里存储的所有映射的视图.*/Set<Map.Entry<K, V>> entrySet();//两个覆盖object的方法/*** 用于对比两个map是否相等.* 如果给定的对象是一个map且两个map的映射一致,则返回true.* 一般,两个map的映射一致,要满足的条件是:m1.entrySet().equals(m2.entrySet())* 这就保证了实现了map接口的不同类对于equals方法的使用才是正确的.*/boolean equals(Object o);/*** 返回map的哈希值,map的哈希值被定义为:这个map的entrySet视图的每一个条目的哈希值的总和.* 这就保证了任意两个map相等,则他们的哈希值一定相等,这也是Object类对哈希值的普遍要求(哈希值作为两个对象相等的必要非充分条件).*/int hashCode();/*** map条目(key-value对),Map.entrySet方法返回的就是map的集合视图,map视图中的元素就是来源于此类.* 获取map条目的唯一方式就是来源于集合视图的迭代器.只有在迭代的过程中,Map.Entry对象才是有效的;* 通常,如果通过迭代器获得的map条目,在遍历过程中,作为后台支持的map被修改了,那么map条目会如何被影响,对此* 并没有做出具体规定(当然此处说的map修改不包括setValue方法的调用).*/interface Entry<K,V> {/*** 获取当前map条目对应的key*/K getKey();/*** 返回map条目对应的value值.*/V getValue();/*** 用指定值替换当前条目中的value*/V setValue(V value);/*** 将指定对象和当前条目做比较,如果给定的对象是一个条目并且两个条目代表同一个映射,则返回true.*/boolean equals(Object o);/*** 返回map条目的哈希值*/int hashCode();/*** 返回一个比较map.entry的比较器,按照key的自然顺序排序,返回的比较器支持序列化* @since 1.8*/public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {return (Comparator<Map.Entry<K, V>> & Serializable)(c1, c2) -> c1.getKey().compareTo(c2.getKey());}/*** 返回一个map.enty的比较器,按照value的自然顺序排序,返回的比较器支持序列化* @since 1.8*/public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {return (Comparator<Map.Entry<K, V>> & Serializable)(c1, c2) -> c1.getValue().compareTo(c2.getValue());}/*** 返回一个map.entry的比较器,根据传入比较器对key排序,如果传入的比较器支持序列化,则返回的结果比较器也支持序列化.* @since 1.8*/public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {Objects.requireNonNull(cmp);return (Comparator<Map.Entry<K, V>> & Serializable)(c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());}/*** 返回一个map.entry的比较器,根据传入比较器对value排序,如果传入的比较器支持序列化,则返回的结果比较器也支持序列化* @since 1.8*/public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {Objects.requireNonNull(cmp);return (Comparator<Map.Entry<K, V>> & Serializable)(c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());}}//1.8新增/*** 返回指定key对应的value,如果没有对应的映射,则返回传入参数中的默认值defaultValue* @since 1.8*/default V getOrDefault(Object key, V defaultValue) {V v;return (((v = get(key)) != null) || containsKey(key))? v: defaultValue;}/*** 对map中每一个entry执行action中定义对操作,直到全部entry执行完成or执行中出现异常为止* @since 1.8*/default void forEach(BiConsumer<? super K, ? super V> action) {Objects.requireNonNull(action);for (Map.Entry<K, V> entry : entrySet()) {K k;V v;try {k = entry.getKey();v = entry.getValue();} catch(IllegalStateException ise) {// this usually means the entry is no longer in the map.throw new ConcurrentModificationException(ise);}action.accept(k, v);}}/*** 对于map中每一个entry,将其value替换成BiFunction接口返回的值.直到所有entry替换完or出现异常为止* @since 1.8*/default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {Objects.requireNonNull(function);for (Map.Entry<K, V> entry : entrySet()) {K k;V v;try {k = entry.getKey();v = entry.getValue();} catch(IllegalStateException ise) {// this usually means the entry is no longer in the map.throw new ConcurrentModificationException(ise);}// ise thrown from function is not a cme.v = function.apply(k, v);try {entry.setValue(v);} catch(IllegalStateException ise) {// this usually means the entry is no longer in the map.throw new ConcurrentModificationException(ise);}}}/*** 如果指定的键尚未与值相关联(或被映射为null),则将它与给定的值相关联并返回null,否则返回当前值。* @since 1.8*/default V putIfAbsent(K key, V value) {V v = get(key);if (v == null) {v = put(key, value);}return v;}/*** 如果给定的参数key和value在map中是一个entry,则删除这个entry.* @since 1.8*/default boolean remove(Object key, Object value) {Object curValue = get(key);if (!Objects.equals(curValue, value) ||(curValue == null && !containsKey(key))) {return false;}remove(key);return true;}/*** 如果给定的key和value在map中有entry,则为指定key的entry,用新value替换旧的value* @since 1.8*/default boolean replace(K key, V oldValue, V newValue) {Object curValue = get(key);if (!Objects.equals(curValue, oldValue) ||(curValue == null && !containsKey(key))) {return false;}put(key, newValue);return true;}/***如果指定key在map中有value,则用参数value进行替换* @since 1.8*/default V replace(K key, V value) {V curValue;if (((curValue = get(key)) != null) || containsKey(key)) {curValue = put(key, value);}return curValue;}/*** 如果指定key在map中没有对应的value,则使用输入参数,即函数接口mappingfunction为其计算一个value.* 如果计算value不为null,则将value插入map中,如果计算function返回结果为null,则不插入任何映射。* @since 1.8*/default V computeIfAbsent(K key,Function<? super K, ? extends V> mappingFunction) {Objects.requireNonNull(mappingFunction);V v;if ((v = get(key)) == null) {V newValue;if ((newValue = mappingFunction.apply(key)) != null) {put(key, newValue);return newValue;}}return v;}/*** 如果map中存在指定key对应的value,且不为null,则本方法会尝试使用function,并利用key生成一个新的value* 如果function接口返回null,map中原entry则被移除.如果function本身抛出异常,则当前map不会发生改变.* @since 1.8*/default V computeIfPresent(K key,BiFunction<? super K, ? super V, ? extends V> remappingFunction) {Objects.requireNonNull(remappingFunction);V oldValue;if ((oldValue = get(key)) != null) {V newValue = remappingFunction.apply(key, oldValue);if (newValue != null) {put(key, newValue);return newValue;} else {remove(key);return null;}} else {return null;}}/*** 利用指定key和value计算一个新映射,比如:向一个value映射中新增或者拼接一个String。* 如果function接口返回null,则map中原entry被移除(如果本来就不存在,则不执行移除操作).* 如果function本身抛出(非检查型)异常,异常会被重新抛出,当前map也不会发生改变.* @since 1.8*/default V compute(K key,BiFunction<? super K, ? super V, ? extends V> remappingFunction) {Objects.requireNonNull(remappingFunction);V oldValue = get(key);V newValue = remappingFunction.apply(key, oldValue);if (newValue == null) {// delete mappingif (oldValue != null || containsKey(key)) {// something to removeremove(key);return null;} else {// nothing to do. Leave things as they were.return null;}} else {// add or replace old mappingput(key, newValue);return newValue;}}/*** 如果指定key没有value,或者其value为null,则将其改为给定的非null的value* 否则,用给定的function返回值替换原value.* 如果给定参数value和function返回结果都为null,则删除map中这个entry.* 这一方法常用于:对一个key合并多个映射的value时.* 比如:要创建或追加一个String给一个值映射.* 如果function返回null,则map中原entry被移除.如果function本身抛出异常,则异常会被重新抛出,且当前map不会发生更改.* @since 1.8*/default V merge(K key, V value,BiFunction<? super V, ? super V, ? extends V> remappingFunction) {Objects.requireNonNull(remappingFunction);Objects.requireNonNull(value);V oldValue = get(key);V newValue = (oldValue == null) ? value :remappingFunction.apply(oldValue, value);if(newValue == null) {remove(key);} else {put(key, newValue);}return newValue;}
}}

上述注释按照JDK进行翻译,如有不正确支出,望提出。

这篇关于吃透Java集合系列八:Map的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1131677

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl