李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
容器深入研究--可选操作
Leefs
2020-01-13 AM
2041℃
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
工具
31
其它
25
GO
47
NLP
4
标签云
并发线程
JavaSE
Flume
前端
Python
LeetCode刷题
MyBatis-Plus
Azkaban
正则表达式
Spring
Yarn
Stream流
哈希表
数据结构和算法
CentOS
nginx
ClickHouse
RSA加解密
Filter
MyBatisX
查找
Elastisearch
Golang基础
Hive
Tomcat
NIO
MySQL
JavaScript
Java阻塞队列
递归
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
评论已关闭