轻松解读源码系列之Java集合接口ampampamp抽象类(1)Map和Collection
大家好,我是程序员xiao熊,本篇内容将为大家分享Java集合的主要接口和抽象类;这些接口和抽象类是Java集合框架的基础,为具体的子类实现提供了规范以及基础实现,降低了子类实现的成本;
Java集合的底层接口主要分为两个:Map和Collection;其对应的继承结构图如下所示;本篇内容主要是对Map、Collection、Iterable 这三个接口以及其中的部分默认方法 进行讲解,其他的接口和抽象类将在后续的文章中进行讲解,会按照Map和Collection体系分别讲解,并且尽量按照图中的层次从上至下,一层层讲解;
Collection体系继承结构图
Map体系继承结构图1、Iterable接口
Iterable接口相对简单,实现这个接口的对象,可以使用for-each循环语句,也可以通过iterator遍历元素1.1 方法说明
default void forEach(Consumer<? super T> action)
对Iterable对象的每个元素执行指定的操作(action),直到所有元素都处理完毕或操作(action)抛出异常(异常抛给调用者)
Iterator iterator()
返回类型为T的元素的迭代器, T是集合元素的类型
default Spliterator spliterator()
返回Spliterator对象 2、Collection接口
集合是一组对象,对象就是集合里面的元素;在JDK中,Collection是集合对应的实现,是集合框架结构中的根接口,它提供了集合应该具备的操作规范(方法);在集合的具体实现类中,有些集合允许重复元素,有些则不允许,有些是有序的,有些是无序的,具体取决于实现类。JDK中的具体实现类并没有直接实现这个接口,因为JDK根据场景提供了更具体的子接口,如Set和List。实现类都是实现这些具体的接口或者对应的抽象方法。Collection这个接口通常用于传递集合,并在需要最大通用性的情况下对集合进行操作。
集合规范中提到,Collection的实现类一般可以提供两个构造函数
1. 无参构造函数:创建一个空的集合
2. 入参类型是Collection的构造函数:目的是创建一个包含入参集合元素的集合(即复制入参集合)
Collection的具体实现类根据场景,在实现Collection接口的方法时,会抛出一些异常,具体如下:
1. 如果collection的实现类不支持的操作,则实现类在方法体中需要抛出UnsupportedOperationException异常
2. Collection的不同实现类对元素类型可能会有限制,例如不能null,或者只接受指定的元素类型;因此可能会抛出非检查异常,例如:NullPointerException或ClassCastException;添加不符合的元素时,可能抛出异常,可能执行成功(只是方法执行成功),但是元素并不会进入到集合中;对于查询不符合条件的元素,会直接返回false
3. Collection默认不支持同步控制,实现类可以自行指定具体使用的同步控制策略;
4. 默认方法实现(继承的或其他方式)没有使用任何同步协议。如果集合的实现类需要使用同步协议,那么它必须覆盖默认方法的实现以使用同步协议。
使用的注意事项:
集合的元素需要注意,如果元素直接或者间接包含了自身引用,则某些对集合执行递归遍历的操作可能会失败(抛出异常),例如clone()、equals()、hashCode()和toString()方法。具体示例如下:// 集合元素 public class SelfRef { SelfRef selfRef; public SelfRef getSelfRef() { return selfRef; } public void setSelfRef(SelfRef selfRef) { this.selfRef = selfRef; } @Override public String toString() { return selfRef.toString(); } } // 集合操作示例代码 ArrayList selfRefList = new ArrayList<>(); SelfRef selfRef = new SelfRef(); selfRef.setSelfRef(selfRef); selfRefList.add(selfRef); selfRef.toString(); // 抛出异常 2.1、方法说明boolean add(E e)
添加元素到集合中boolean addAll(Collection<? extends E> c)
添加一个集合中的元素到当前集合中void clear()
清除集合中的元素boolean contains(Object o)
判断集合是否包含指定的对象boolean containsAll(Collection<?> c)
判断是否包含指定集合中所有的元素 boolean equals(Object o)
判断是否与指定的对象相等int hashCode()
返回集合的hash码boolean isEmpty()
判断集合是否为空Iterator iterator()
返回包含元素的iterator对象default Stream parallelStream()
返回并发执行的流对象(这是默认方法,已有具体逻辑)boolean remove(Object o)
删除指定的元素boolean removeAll(Collection<?> c)
删除存在于指定集合中所有的元素 default boolean removeIf(Predicate<? super E> filter)
根据谓词对象删除符合条件的元素 (这是默认方法,已有逻辑)boolean retainAll(Collection<?> c)
保留存在于指定集合中的所有元素int size()
返回返回集合中元素的数量default Spliterator spliterator()
返回包含元素的spliterator对象(这是默认方法,已有逻辑)default Stream stream()
返回包含元素的流对象(这是默认方法,已有逻辑)Object[] toArray()
将元素以Object数组形式返回,返回的数组与集合互不影响
T[] toArray(T[] a)
返回指定类型的数组,返回的数组与集合互不影响2.2、方法解析
对于Collection接口,本小节对removeIf方法进行分析说明,该方法是删除集合中符合谓词表达式的元素;其业务逻辑过程如下
1. 谓词对象非空校验
2. 获取迭代器iterator
3. 利用while循环和iterator遍历链表的中的元素
4. 利用谓词对象的test方法验证元素是否符合谓词表达式,符合,则利用iterator删除该元素,然后返回步骤3,进行下一轮循环;
removeIf方法 具体代码如下:default boolean removeIf(Predicate<? super E> filter) { // 1. 谓词对象非空校验 Objects.requireNonNull(filter); boolean removed = false; // 2. 获取迭代器iterato final Iterator each = iterator(); // 3. 利用while循环和iterator遍历链表的中的元素 while (each.hasNext()) { // 4.利用谓词对象的test方法验证元素是否符合谓词表达式,符合,则利用iterator删除该元素, // 然后返回步骤3,进行下一轮循环 if (filter.test(each.next())) { each.remove(); removed = true; } } return removed; }3、Map
Map是将key和value进行映射的对象;一个Map对象不能包含重复的key值,一个key最多只能映射到一个value,即key 与value是一一对应的;Map接口的出现是对抽象类Dictionary的替代;
Map接口提供了三个视图:key的集合视图,value的集合视图,key-value映射关系的集合视图 ;Map的顺序是指Map集合视图中迭代器返回元素的顺序。有些Map接口的实现类会明确指定元素的顺序,比如TreeMap类;其他实现类则不一定能够保证元素的顺序是固定的,如HashMap类。
集合规范中提到,Map实现类 一般可以提供两个构造函数:
1. 无参构造函数,用于创建一个空的Map;
2. 只有一个Map类型的参数的构造函数:用于创建一个新的Map,其key-value的映射关系与参数Mapd对象相同。实际上是允许用户复制任何Map,生成一个等效的Map。
Map的实现类根据不同的场景使用不同的实现策略,总体原则如下:
1. 如果不支持修改Map的方法,则在操作修改Map的方法时,会抛出UnsupportedOperationException异常。
2. 有些Map的实现对包含的key和value有限制。例如,有些实现类禁止空的key和空value,有些实现类对key的类型有限制。尝试插入不符合条件的key或value会抛出未检异常,通常是NullPointerException或ClassCastException。查询不符合条件的key或value可能会抛出异常,也可能直接返回false。更一般地说,如果对不符合条件的key或value进行操作,而操作可能会抛出异常,也可能会成功,这取决于实现方式; 但操作无论成功还是失败都不会导致不符合条件的元素插入到Map中,则这种异常在此接口的规范中标记为"可选"。
使用中的注意事项:
1. 如果使用可变对象作为Map的键,则必须要注意。因为如果一个对象的值发生了改变(这个会影响equals方法的比较结果),而该对象是Map中的key,那么Map的行为将会变得不确定。还有一个特殊情况是,不允许Map将自身作为key。虽然Map允许将自身包含为一个value,但我们要特别小心:equals和hashCode方法的定义已经不适用这样的Map了。
2. 一些对Map进行递归遍历的操作可能会失败,当map直接或间接包含自己时,会出现自引用实例的异常。其中包括clone()、equals()、hashCode()和toString()方法。具体如下: // 元素 public class SelfRef { SelfRef selfRef; public SelfRef getSelfRef() { return selfRef; } public void setSelfRef(SelfRef selfRef) { this.selfRef = selfRef; } @Override public String toString() { return selfRef.toString(); } } // 示例代码 Map selfRefMap = new HashMap<>(); selfRefMap.put("selfRef", selfRef); selfRefMap.toString();// 抛出异常 3.1、方法说明void clear()
清除Map中的元素default V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
尝试为指定的key及其当前的value(如果key不在Map中则value为空)计算新的value值;如果key在Map中,并且新的value不为空,则更新value值,否则删除key对应元素
如果Key不在Map中,新的value不为空,则将key和新的value加入到Map中,如果新的value为空,则Map保持不变default V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
为指定的key计算新的value值;如果key不在Map中,或则映射到null,并且新的value不为null,则加入Map;否则Map保持不变default V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
为指定的key计算新的value值;如果key在Map中,并且对应的value不为空;在新的value不为null时,则Map中key对应的value;否则删除Map中的keyboolean containsKey(Object key)
判断是否包含指定的keyboolean containsValue(Object value)
判断是否包含指定的valueSet> entrySet()
返回key-value对象的集合视图boolean equals(Object o)
判断当前Map是否等于指定的对象default void forEach(BiConsumer<? super K,? super V> action)
循环对元素执行指定的操作actionV get(Object key)
获取指定key对应的value,如果key不存在,则返回nulldefault V getOrDefault(Object key, V defaultValue)
获取指定key对应的value,如果key不存在,则返回指定的默认值defaultValueint hashCode()
返回Map的hashcodeboolean isEmpty()
判断Map是否为空,但map为null,则会抛出异常
Set keySet()
返回Key对象的集合视图
default V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)
计算key对应的value值,如果原的value为null或者key不存在,则将参数的value作为key的新value;如果key对应的原value不为空,则通过remappingFunction重新计算新的value值,如果新的value值不为空,则更新key的value值或者将key添加至集合中,如果新的value为空,则删除key对应的元素
V put(K key, V value)
将Key与value进行关联
Void putAll(Map<? extends K,? extends V> m)
将入参Map中key-value对象复制到当前的Map中
default V putIfAbsent(K key, V value)
为当前存在于Map中的key关联新的value值
V remove(Object key)
删除指定的key对应的元素
default Boolean remove(Object key, Object value)
删除Map中key和value与指定的key和value匹配的元素
default V replace(K key, V value)
替换key对应的value,如果key不存在,则不操作
default Boolean replace(K key, V oldValue, V newValue)
如果key的原value与入参oldvalue匹配,则替换key的value为newValue,如果不满足,则不操作
default void replaceAll(BiFunction<? super K,? super V,? extends V> function)
将Map中所有的key的value进行更新,新的value由入参指定的函数确定
Int size()
返回Map中元素刷领
Collection values()
返回value的集合视图3.2、方法解析
jdk1.8开始,接口允许默认方法有方法,本小节针对以下几个默认方法进行讲解分析:
1. compute(…)
2. computeIfAbsent(…)
3. computeIfPresent(…)
4. merge(…)3.2.1、Compute方法
Compute方法是根据当前的key以及对应value,利用remappingFunction对象计算新的value值,具体业务逻辑如下:
1. remappingFunction对象的非空校验,remappingFunction对象是用于计算新的value的值
2. 根据key获取对应的value
3. 根据key、原有的value(oldValue),通过remappingFunction的apply方法计算新的value值
4. 新的value值(newValue)等于null,
4.1. 如果key存在于Map中,则需要删除key对应的元素,返回null;
4.2. 如果key不在Map中,则不处理,Map保持不变,返回null;
5. 新的value值不等于null,则将key和新的value值存入Map中,返回新的value值
代码如下:default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { // 1. remappingFunction对象的非空校验,remappingFunction对象是用于计算新的value的值 Objects.requireNonNull(remappingFunction); // 2. 根据key获取对应的value V oldValue = get(key); // 3. 根据key、原有的value(oldValue),通过remappingFunction的apply方法计算新的value值 V newValue = remappingFunction.apply(key, oldValue); // 4. 新的value值(newValue)等于null if (newValue == null) { // 4.1如果key存在于Map中,则需要删除key对应的元素,返回null; if (oldValue != null || containsKey(key)) { // something to remove remove(key); return null; } else { // 4.2 如果key不在Map中,则不处理,Map保持不变,返回null. return null; } } else { // 5.新的value值不等于null,则将key和新的value值存入Map中,返回新的value值 // add or replace old mapping put(key, newValue); return newValue; } } 3.2.2、computeIfAbsent
computeIfAbsent与Compute类似,在新的value值不为null的前提下,只有在key不存在Map中,或者key对应的原value为null时,才会更新key的value值为新的值;具体业务逻辑如下:
1. mappingFunction对象的非空校验,mappingFunction对象是用于计算新的value的值
2. 根据key获取对应的value
3. 如果value为null(表示key不存在,或者对应的value为null),则根据key,通过mappingFunction的apply方法计算新的value值
4. 新的value值(newValue)不等于null, 则将key和新的value值存入Map中,返回新的value值
代码如下:default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { // 1.mappingFunction对象的非空校验 Objects.requireNonNull(mappingFunction); V v; // 2. 根据key获取对应的value if ((v = get(key)) == null) { V newValue; // 3.如果value为null(表示key不存在,或者对应的value为null), //则根据key,通过mappingFunction的apply方法计算新的value值 if ((newValue = mappingFunction.apply(key)) != null) { // 4. 新的value值(newValue)不等于null, // 则将key和新的value值存入Map中,返回新的value值 put(key, newValue); return newValue; } } return v; } 3.2.3、computeIfPresent
computeIfPresent与Compute类似,在key存在Map中的前提下,新的value不为null时,才会更新key的value值为新的值,新的value为null时,删除key对应的元素;具体业务逻辑如下:
1. remappingFunction对象的非空校验,remappingFunction对象是用于计算新的value的值
2. 根据key获取对应的value
3. 如果原有的value不为null,才处理新的值
3.1. 根据key、原有的value(oldValue),通过remappingFunction的apply方法计算新的value值
3.2. 新的value值不等于null,则将key和新的value值存入Map中,返回新的value值
3.3. 新的value值(newValue)等于null,则需要删除key对应的元素,返回null;
4. 如果原有的value为null,或者key不存在,则不做任何操作,直接返回nulldefault V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { // 1 remappingFunction对象的非空校验 Objects.requireNonNull(remappingFunction); V oldValue; // 2 根据key获取对应的value // 3 如果原有的value不为null,才处理新的值 if ((oldValue = get(key)) != null) { // 3.1 根据key、原有的value(oldValue),通过remappingFunction的apply方法计算新的value值 V newValue = remappingFunction.apply(key, oldValue); // 3.2新的value值不等于null,则将key和新的value值存入Map中,返回新的value值 if (newValue != null) { put(key, newValue); return newValue; } else { // 3.3 新的value值(newValue)等于null,则需要删除key对应的元素,返回null; remove(key); return null; } } else { // 4 如果原有的value为null,或者key不存在,则不做任何操作,直接返回null return null; } } 3.2.4、merge
merge方法生成value的方式比之前的所有类似;计算新的value由两种方式将入参的第二个参数作为新的value值根据第二个参数value和原value,利用remappingFunction对象计算新的value的值
具体业务逻辑过程如下:
1. remappingFunction对象的非空校验
2. 方法的value对象(第二个参数)的非空校验
3. 根据key获取对应的原value
4. 如果原value为null,则取入参的第二个参数value作为新的值;如果原有的value不为null,则使用原有的value、入参的value(第二个参数),通过remappingFunction计算新的value
5. 如果新的value为null,将key从Map中删除,返回新的value值(为null)
6. 如果新的value不为null,将key和新的value值存入Map中,返回新的value值
具体代码如下:default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { // 1 remappingFunction对象的非空校验 Objects.requireNonNull(remappingFunction); // 2 方法的value对象(第二个参数)的非空校验 Objects.requireNonNull(value); // 3 根据key获取对应的原value V oldValue = get(key); // 4 如果原value为null, 则取入参的第二个参数value作为新的值;如果原有的value不为null,则使用原有的value、入参的value(第二个参数),通过remappingFunction计算新的value V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value); //5. 如果新的value为null,将key从Map中删除,返回新的新的value值 if(newValue == null) { remove(key); } else { // 6. 如果新的value不为null,将key和新的value值存入Map中,返回新的value值 put(key, newValue); } return newValue; }
欢迎大家关注【程序员xiao熊】,今天的分享就到这里,欢迎大家在评论区进行交流
SpringBoot2。x系列教程54SpringBoot整合日志记录项目重要信息前言我们在进行项目开发时,无论是前端还是后端,都必须进行日志的记录。通过日志,来记录项目开发运行时产生的各种异常信息和重要数据。这样我们才能对项目的异常进行定位,对项目的后期运营提
余生最好的活法顺境做事,逆境读书,平境惜福作者洞见yimo物来顺应,相时而动。曾国藩说凡成大事,人谋居半,天意居半。人力有时尽,很多时候,成事不仅靠个人的努力,也要遵从大势和规律。趁火要打铁,趁水须和泥。学会审时度势,不同
听得出你嗓音嘶哑周围的人纷纷中招,你也阳了这么一把。经过一周多的抗争,病毒终于被你打趴退烧啦,止疼啦,有点口味能吃啦!牵挂地球那边的你,昨晚与你语音通话。你说好得差不多了,听得出你嗓音嘶哑。你说这
招聘试睡员是幌子,其实是诈骗团伙在雇帮凶视频加载中现代快报讯(通讯员广公轩记者顾潇)近年来,试睡员成了一种颇为传奇的新职业,从业者出入各大宾馆体验撰写住店报告,动辄月薪过万,被网友称作史上最爽职业。然而,扬州警方近日发现
史诗大作变成伦理片,卡梅隆这次失手了2009年,阿凡达横空出世,凭一己之力推动了3D技术在中国的狂飙突进。2022年,阿凡达水之道(以下简称水之道)终于在五年五年又三年后上映了。当第一批影迷冒着发烧的风险全副武装进入
扬州四岸公所修缮完成用技术重现记忆南河下一直是扬州老城区的聚宝盆,藏着小盘谷贾氏盐商住宅等深宅大院,近年这些老建筑历经修缮,已重焕青春。最近有好消息传来又一处市级文保单位扬州四岸公所修缮完成。为我市打造的盐商文化旅
苏轼相识满天下,不如知己三两人作者洞见安娜贝苏大喜大悲看清自己,大起大落看清朋友。红楼梦里说万两黄金容易得,知己一个也难求。苏轼这一生,相识满天下。但大多数人都只是他人生的旁观者,笑看他起落沉浮,辗转飘零。只有
生小宝宝,究竟是选择顺产还是剖腹产?孕妇在孕晚期比较关心的就是顺产和剖腹产该怎么选择。对于第一次怀孕的孕妈妈来说,不知道该选择哪种分娩方式了。那么是顺产还是剖腹产对孩子好?其实顺产和剖腹产都各自有好处和坏处。如果身体
孕囊旁积液是血吗?在妊娠期间,尤其是妊娠早期,B超检查会发现孕囊周围存在液性暗区,报告结果提示孕囊周围有积液,其实就是孕早期孕囊周围的出血,也称为孕囊旁积血。多属于植入性出血。说明孕囊在子宫中种植不
可从睡觉看智商如果宝宝睡着后有以下行为,暗示你家娃挺聪明的三个月前,宝宝睡觉一切都好,一直都保持同样的姿势。但是三个月之后,宝宝仿佛打通了任督二脉一样,睡觉的姿势千奇百怪,一直到现在一岁多。可以说解锁了上百种睡觉姿势,真的让人大开眼界。每
新生儿抚触很重要保姆级教程新手爸妈必学给新生儿宝宝做抚触真的太重要啦宝宝做抚触的好处非常多,从新生儿阶段就开始给宝宝按摩的话,宝宝不仅会从妈妈的手中感受到安全感,安定宝宝情绪,促进宝宝睡眠,缓解肠胀气,还能促进亲子关系