李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
容器深入研究--可选操作
Leefs
2020-01-13 AM
2643℃
0条
# 容器深入研究--可选操作 ### 前言 本篇讲述《Java编程思想》第17.4小节,可选操作 ### 概述 执行各种不同的添加和移除的方法在Collection接口中都是可选操作。这意味着实现类并不需要这些方法提供功能定义。 **可选操作的具体实现与表现:**一般是在Abstract类中实现特定的方法,但是该方法体内只有一条抛出`UnsupportOperationException`异常语句;从而继承该抽象类却没override该方法的类,在进行向上转型使用root interface来调用该方法时,在运行时抛出异常。 定义可选操作的原因:“这样做可以防止在设计中出现接口爆炸的情况”。通过抽象类来推迟某些方法的实现,直到需要实现的时候。 但是为了让这种方式能够工作: > 1. 1.`UnsupportedOperationException` 必须是一种罕见事件。即,对于大多数类而言,所有操作都应该可以工作,只有在特例(例如,通过`Arrays.asList()`所得到的容器)中才会有未获支持的操作。在 Java 容器类库中确实如此,因为你在 99% 的时间里使用的容器类,如 `ArrayList`、`LinkedList`、`HashSet` 和 `HashMap`,以及其他的具体实现,都支持所有操作。这种设计留下了一个“后门”,如果你想创建新的 Collection, 但是没有为 Collection 接口中的所有方法都提供有意义的定义,那么它仍旧适合现有类库。 > 2. 2.如果一个操作是未获支持的,那么在实现接口的时候可能就会导致 `UnsupportedOperationException` 异常,而不是将产品程序交给客户后才出现此异常,这种情况是有道理的。毕竟,他表示编程上有错误:使用了不正确的接口实现。 值得注意的是,未获支持操作只有在运行时才能探测到,因此他们表示动态类型检查。 ### 未获支持操作 **最常见的未获支持的操作,都来源于背后由固定尺寸的数据结构支持的容器。**当你用 `Arrays.asList()` 将数组转换为 List 时,就会得到这样的容器。此外,你还可以通过使用 Collection 类中“不可修改”的方法,选择创建任何会抛出会抛出 `UnsupportedOperationException` 的容器。 **示例** ```java public class Unsupported { static void test(String msg, List
list) { System.out.println("--- " + msg + " ---"); Collection
c = list; Collection
subList = list.subList(1, 8); // Copy of the sublist: Collection
c2 = new ArrayList
(subList); try { c.retainAll(c2); } catch (Exception e) { System.out.println("retainAll(): " + e); } try { c.removeAll(c2); } catch (Exception e) { System.out.println("removeAll(): " + e); } try { c.clear(); } catch (Exception e) { System.out.println("clear(): " + e); } try { c.add("X"); } catch (Exception e) { System.out.println("add(): " + e); } try { c.addAll(c2); } catch (Exception e) { System.out.println("addAll(): " + e); } try { c.remove("C"); } catch (Exception e) { System.out.println("remove(): " + e); } // The List.set() method modifies the value but // doesn’t change the size of the data structure: try { list.set(0, "X"); } catch (Exception e) { System.out.println("List.set(): " + e); } } public static void main(String[] args) { List
list = Arrays.asList("A B C D E F G H I J K L".split(" ")); test("Modifiable Copy", new ArrayList
(list)); // 产生新的尺寸可调的 // ArrayList test("Arrays.asList()", list); // 产生固定尺寸的 ArrayList test("unmodifiableList()", Collections.unmodifiableList(new ArrayList
(list))); // 产生不可修改的列表 } } ``` > 运行结果 ```visual basic --- Modifiable Copy --- --- Arrays.asList() --- retainAll(): java.lang.UnsupportedOperationException removeAll(): java.lang.UnsupportedOperationException clear(): java.lang.UnsupportedOperationException add(): java.lang.UnsupportedOperationException addAll(): java.lang.UnsupportedOperationException remove(): java.lang.UnsupportedOperationException --- unmodifiableList() --- retainAll(): java.lang.UnsupportedOperationException removeAll(): java.lang.UnsupportedOperationException clear(): java.lang.UnsupportedOperationException add(): java.lang.UnsupportedOperationException addAll(): java.lang.UnsupportedOperationException remove(): java.lang.UnsupportedOperationException List.set(): java.lang.UnsupportedOperationException ``` **因为 `Arrays.asList()` 实际上会产生一个 `Arraylist` ,它基于一个固定大小的数组,仅支持那些不会改变数组大小的操作。所以,任何会引起对底层数据结构的尺寸进行修改的方法(add/remove 相关)都会产生一个 `UnsupportedOperationException` 异常,以表示对该容器未获支持操作的调用。** 因此,**`Arrays.asList()` 的真正意义在于:将其结果作为构造器参数传递给任何 Collection (或者使用 `addAll` 方法、`Collections.addAll` 静态方法),这样可以生成一个动态的容器**。 由以上程序片段可知, **`Arrays.asList()` 返回固定尺寸的List,而 `Collections.unmodifiableList()` 产生不可修改的列表。正如输出所示,前者支持 set 操作,而后者不支持。若使用接口,那么还需要两个附加的接口,一个具有可以工作的 set 方法,另一个没有,因为附加的接口对于 Collection 的各种不可修改子类型来说是必须的。因此,可选方法可以避免** **接口爆炸**。 ### `Arrays.asList()`源码解析 1.首先该方法的源码为: ```java public static
List
asList(T... a) { return new ArrayList
(a); } ``` 2.上述方法所返回的由固定尺寸的数据结构支持的容器源码(部分): ```java private static class ArrayList
extends AbstractList
implements RandomAccess, java.io.Serializable { private static final long serialVersionUID = -2764017481108945198L; private final E[] a; ArrayList(E[] array) { if (array==null) throw new NullPointerException(); a = array; } public int size() { return a.length; } public Object[] toArray() { return a.clone(); } public
T[] toArray(T[] a) { int size = size(); if (a.length < size) return Arrays.copyOf(this.a, size, (Class extends T[]>) a.getClass()); System.arraycopy(this.a, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } public E get(int index) { return a[index]; } public E set(int index, E element) { //该容器支持的操作 E oldValue = a[index]; a[index] = element; return oldValue; } public int indexOf(Object o) { if (o==null) { for (int i=0; i
c) { boolean modified = false; Iterator extends E> e = c.iterator(); while (e.hasNext()) { add(index++, e.next()); modified = true; } return modified; } ``` **针对 remove 操作:** 该容器的 remove 相关行为间接继承自 `AbstractCollection` 类, `AbstractList` 类并未完全重写(只重写了 clear 操作)它们,如下: ```java //AbstractList 类中方法: //该方法继承自 List 接口的:remove(int index) 方法,是 List 特有的方法 public E remove(int index) { throw new UnsupportedOperationException(); } //AbstractList 类重写了 AbstractCollection 的 clear 方法 public void clear() { removeRange(0, size()); } protected void removeRange(int fromIndex, int toIndex) { ListIterator
it = listIterator(fromIndex); for (int i=0, n=toIndex-fromIndex; i
e = iterator(); if (o==null) { while (e.hasNext()) { if (e.next()==null) { e.remove(); return true; } } } else { while (e.hasNext()) { if (o.equals(e.next())) { e.remove(); //直接调用了具体容器的迭代器(由 iterator() 方法返回)的 remove 方法 return true; } } } return false; } public boolean removeAll(Collection> c) { boolean modified = false; Iterator> e = iterator(); while (e.hasNext()) { if (c.contains(e.next())) { e.remove(); //直接调用了具体容器的迭代器(由 iterator() 方法返回)的 remove 方法 modified = true; } } return modified; } public boolean retainAll(Collection> c) { boolean modified = false; Iterator
e = iterator(); while (e.hasNext()) { if (!c.contains(e.next())) { e.remove(); //直接调用了具体容器的迭代器(由 iterator() 方法返回)的 remove 方法 modified = true; } } return modified; } ``` 由上面两段代码可知,remove(Object),`removeAll(Collection)` ,`retainAll(Collection)` 和 clear() 这四种操作均直接调用了具体容器的迭代器(由 iterator() 方法返回)的 remove 方法 ,我们再看 `AbstractList` 类提供的 Iterator 中的 remove 方法: ```java private class Itr implements Iterator
{ /** * Index of element to be returned by subsequent call to next. */ int cursor = 0; /** * Index of element returned by most recent call to next or * previous. Reset to -1 if this element is deleted by a call * to remove. */ int lastRet = -1; /** * The modCount value that the iterator believes that the backing * List should have. If this expectation is violated, the iterator * has detected concurrent modification. */ int expectedModCount = modCount; public boolean hasNext() { return cursor != size(); } public E next() { checkForComodification(); try { E next = get(cursor); lastRet = cursor++; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } public void remove() { if (lastRet == -1) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.remove(lastRet); //直接调用 AbstractList 容器的 remove(int index) 方法,抛出 UnsupportedOperationException if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } } ``` 综上可知,示例中的 Arrays.asList() 部分为何会导致那样的结果。 *附:[参考文章链接](https://blog.csdn.net/DjokerMax/article/details/81538898)*
标签:
Java
,
Java编程思想
,
容器深入研究
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/413.html
上一篇
持续集成工具--Jenkins简介
下一篇
容器深入研究--List的功能方法
取消回复
评论啦~
提交评论
栏目分类
随笔
2
Java
326
大数据
229
工具
35
其它
25
GO
48
NLP
8
标签云
VUE
算法
排序
Kubernetes
字符串
SpringCloudAlibaba
高并发
前端
Golang
持有对象
Spark
DataX
MySQL
SpringCloud
Java
Git
Java阻塞队列
Quartz
Ubuntu
Jquery
数据结构
Java编程思想
链表
Prometheus
MyBatis-Plus
Http
递归
Docker
Linux
RSA加解密
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞