热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【“计算机科学与技术”专业小白成长系列】HashMap取Keys集合addAll操作的一个坑

HashMap取Keys集合addAll操作的一个坑错误代码实例packagecom.light.swordfunmain(){valm1hashMapOf(1toA,2to
HashMap 取 Keys 集合 addAll 操作的一个坑


错误代码实例

package com.light.swordfun main() {val m1 = hashMapOf(1 to "A", 2 to "B", 3 to "C")val m2 = hashMapOf(3 to "C", 4 to "D", 5 to "E")val s1 = m1.keysval s2 = m2.keyss1.addAll(s2) // java.lang.UnsupportedOperationExceptionprintln(s1)
}

直接抛出异常:

Exception in thread "main" java.lang.UnsupportedOperationExceptionat java.util.AbstractCollection.add(AbstractCollection.java:262)at java.util.AbstractCollection.addAll(AbstractCollection.java:344)at com.light.sword.SetDemoKt.main(SetDemo.kt:18)at com.light.sword.SetDemoKt.main(SetDemo.kt)Process finished with exit code 1

正确的使用方式

fun test2(){val m1 &#61; hashMapOf(1 to "A", 2 to "B", 3 to "C")val m2 &#61; hashMapOf(3 to "C", 4 to "D", 5 to "E")val s1 &#61; m1.keysval s2 &#61; m2.keysval s &#61; HashSet<Int>()s.addAll(s1)s.addAll(s2)println(s) // [1, 2, 3, 4, 5]
}

夸一下 Kotlin 的集合类的设计

Kotlin 在设计 Map 的时候就避免了这个坑&#xff1a;
在这里插入图片描述
不提供 addAll() 方法。 这个 keys 的属性集合是一个只读的集合。

// Views/*** Returns a read-only [Set] of all keys in this map.*/public val keys: Set<K>

在这里插入图片描述

报错原因分析&#xff1a;

Debug 进去看看

addAll:344, AbstractCollection
在这里插入图片描述

这个 e 是什么玩意儿&#xff1f;

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

AbstractCollection 类的结构图&#xff1a;
在这里插入图片描述

HashMap 中的KeySet 类的继承体系是:

在这里插入图片描述
继承体系&#xff1a;
在这里插入图片描述

package java.util;/*** This class provides a skeletal implementation of the Collection* interface, to minimize the effort required to implement this interface.

** To implement an unmodifiable collection, the programmer needs only to* extend this class and provide implementations for the iterator and* size methods. (The iterator returned by the iterator* method must implement hasNext and next.)

** To implement a modifiable collection, the programmer must additionally* override this class&#39;s add method (which otherwise throws an* UnsupportedOperationException), and the iterator returned by the* iterator method must additionally implement its remove* method.

** The programmer should generally provide a void (no argument) and* Collection constructor, as per the recommendation in the* Collection interface specification.

** The documentation for each non-abstract method in this class describes its* implementation in detail. Each of these methods may be overridden if* the collection being implemented admits a more efficient implementation.

** This class is a member of the* * Java Collections Framework.** &#64;author Josh Bloch* &#64;author Neal Gafter* &#64;see Collection* &#64;since 1.2*/public abstract class AbstractCollection<E> implements Collection<E>

在这里插入图片描述
AbstractCollection 实现了一些方法&#xff0c;也定义了几个抽象方法留给子类实现&#xff0c;因此它是一个抽象类。

抽象方法:

public abstract Iterator<E> iterator();
public abstract int size();

子类必须以自己的方式实现这两个方法。除此外&#xff0c;AbstractCollection 中默认不支持添加单个元素&#xff0c;如果直接调用 add(E) 方法&#xff0c;会报错&#xff1a;

public boolean add(E object) {throw new UnsupportedOperationException();
}

因此&#xff0c;如果子类是可添加的数据结构&#xff0c;需要自己实现 add(E) 方法。

实现的方法

1.addAll() 添加一个集合内的全部元素:

public boolean addAll(Collection<? extends E> collection) {boolean result &#61; false;//获取待添加对象的迭代器Iterator<? extends E> it &#61; collection.iterator();while (it.hasNext()) {//挨个遍历&#xff0c;调用 add() 方法添加&#xff0c;因此如果没有实现 add(E) 方法&#xff0c;addAll() 也不能用if (add(it.next())) {result &#61; true;}}return result;
}

2.clear() 删除所有元素&#xff1a;

public void clear() {//获取子类实现的迭代器&#xff0c;挨个遍历&#xff0c;删除Iterator<E> it &#61; iterator();while (it.hasNext()) {it.next();//单线程使用迭代器的 remove() 方法不会导致 fail-fastit.remove();}
}

3.contains() 是否包含某个元素&#xff1a;

public boolean contains(Object object) {//获取子类实现的迭代器&#xff0c;挨个遍历&#xff0c;比较Iterator<E> it &#61; iterator();if (object !&#61; null) {while (it.hasNext()) {//这个元素的类 需要重写 equals() 方法&#xff0c;不然结果够呛if (object.equals(it.next())) {return true;}}} else {//目标元素是空也能查找&#xff0c;说明 AbstractCollection 默认是支持元素为 null 的while (it.hasNext()) {if (it.next() &#61;&#61; null) {return true;}}}return false;
}

4.containsAll() 是否包含指定集合中的全部元素:

public boolean containsAll(Collection<?> collection) {Iterator<?> it &#61; collection.iterator();//挨个遍历指定集合while (it.hasNext()) {//contails 里也是遍历&#xff0c;双重循环&#xff0c;O(n^2)if (!contains(it.next())) {return false;}}return true;
}

5.isEmpty() 是否为空:

public boolean isEmpty() {//调用子类实现的 size() 方法return size() &#61;&#61; 0;
}

6.remove() 删除某个元素:

public boolean remove(Object object) {//获取子类实现的 迭代器Iterator<?> it &#61; iterator();if (object !&#61; null) {while (it.hasNext()) {if (object.equals(it.next())) {it.remove();return true;}}} else {while (it.hasNext()) {if (it.next() &#61;&#61; null) {it.remove();return true;}}}return false;
}

受不了了&#xff0c;又是 if-else&#xff0c;直接写成这样看着不是更舒服吗&#xff1a;

public boolean remove(Object object) {Iterator<?> it &#61; iterator();while (it.hasNext()) {if (object !&#61; null ? object.equals(it.next()) : it.next() &#61;&#61; null) {it.remove();return true;}}return false;
}

7.removeAll() 删除指定集合中包含在本集合的元素&#xff1a;

public boolean removeAll(Collection<?> collection) {boolean result &#61; false;Iterator<?> it &#61; iterator();while (it.hasNext()) {//双重循环if (collection.contains(it.next())) {it.remove();result &#61; true;}}return result;
}

8.retainAll() 保留共有的&#xff0c;删除指定集合中不共有的&#xff1a;

public boolean retainAll(Collection<?> collection) {boolean result &#61; false;Iterator<?> it &#61; iterator();while (it.hasNext()) {//排除异己&#xff0c;不在我集合中的统统 886if (!collection.contains(it.next())) {it.remove();result &#61; true;}}return result;
}

9.toArray(), toArray(T[] contents) 转换成数组&#xff1a;

public Object[] toArray() {
//把集合转换成 ArrayList&#xff0c;然后再调用 ArrayList.toArray() return toArrayList().toArray();
}public <T> T[] toArray(T[] contents) {return toArrayList().toArray(contents);
}&#64;SuppressWarnings("unchecked")
private ArrayList<Object> toArrayList() {ArrayList<Object> result &#61; new ArrayList<Object>(size());for (E entry : this) {result.add(entry);}return result;
}

ArrayList, 集合与数组的桥梁。

10.toString() 把内容转换成一个 String 进行展示:

public String toString() {if (isEmpty()) {return "[]";}//注意默认容量是 size() 的 16 倍&#xff0c;为什么是 16 呢?StringBuilder buffer &#61; new StringBuilder(size() * 16);buffer.append(&#39;[&#39;);//仍旧用到了迭代器Iterator<?> it &#61; iterator();while (it.hasNext()) {Object next &#61; it.next();if (next !&#61; this) {//这个 Object 也得重写 toString() 方法&#xff0c;不然不能输出内容buffer.append(next);} else {buffer.append("(this Collection)");}if (it.hasNext()) {buffer.append(", ");}}buffer.append(&#39;]&#39;);return buffer.toString();
}

我们之所以可以使用 System.out.print() 直接输出集合的全部内容&#xff0c;而不用挨个遍历输出&#xff0c;全都是 AbstractCollection 的功劳&#xff01;

List list &#61; new LinkedList();System.out.println(list);

11.其他

AbstractCollection 默认的构造函数是 protected:

/*** Sole constructor. (For invocation by subclass constructors, typically* implicit.)*/
protected AbstractCollection() {
}

在AbstractCollection的注释文档中提到&#xff1a;

To implement an unmodifiable collection, the programmer needs only to extend this class and provide implementations for the iterator and size methods.(The iterator returned by the iterator method must implement hasNext and next.)
To implement a modifiable collection, the programmer must additionally override this class’s add method (which otherwise throws an UnsupportedOperationException), and the iterator returned by the iterator method must additionally implement its remove method.

由此可以看出&#xff0c;AbstractCollection将其实现类分成两种&#xff0c;一种为只读集合&#xff0c;另一种为可修改集合。

在只读集合中&#xff0c;只需要实现AbstractCollection中的iterator函数和size函数即可&#xff0c;其它的函数可以维持不变&#xff08;在对性能没要求的前提下&#xff09;&#xff0c;这保证了实现类只需要少量的工作&#xff0c;便可以将集合的功能基本实现。

而对于可修改集合&#xff0c;AbstractCollection要求不仅需要实现其中的两个抽象方法&#xff0c;还需要实现add方法&#xff0c;并保证iterator函数返回的迭代器中实现了remove方法。

对于非抽象算法&#xff0c;AbstractCollection的建议为&#xff1a;如果有更加高效的实现方法&#xff0c;子类可以将其重写(override)&#xff0c;建议原文如下&#xff1a;

The documentation for each non-abstract method in this class describes its implementation in detail. Each of these methods may be overridden if the collection being implemented admits a more efficient implementation.




Kotlin 开发者社区

Kotlin 开发者社区

国内第一Kotlin 开发者社区公众号&#xff0c;主要分享、交流 Kotlin 编程语言、Spring Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。


推荐阅读
  • vue使用
    关键词: ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
author-avatar
中青33期_840
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有