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>
Debug 进去看看
addAll:344, AbstractCollection
这个 e 是什么玩意儿&#xff1f;
AbstractCollection 类的结构图&#xff1a;
HashMap 中的KeySet 类的继承体系是:
继承体系&#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&#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*/package java.util;/*** This class provides a skeletal implementation of the Collection* interface, to minimize the effort required to implement this interface.
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 开发者社区公众号&#xff0c;主要分享、交流 Kotlin 编程语言、Spring Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。