本章示例代码来自java编程思想——17.4.1未获支持的操作——Unsupported类。
import java.util.*;
public class Unsupported {
static void test(String msg, List<String> list) {
System.out.println("--- " + msg + " ---");
Collection<String> c = list;
Collection<String> subList = list.subList(1,8);
// Copy of the sublist:
Collection<String> c2 = new ArrayList<String>(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<String> list =
Arrays.asList("A B C D E F G H I J K L".split(" "));
test("Modifiable Copy", new ArrayList<String>(list));
test("Arrays.asList()", list);
test("unmodifiableList()",
Collections.unmodifiableList(
new ArrayList<String>(list)));
}
} /* Output:
--- 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方法生成了一个List
。在调用静态函数test方法时,先后测试了3种List:
ArrayList
的构造器,构造器签名是:public ArrayList(Collection extends E> c)
。Arrays
工具类的静态方法asList
方法,其方法签名是public static List asList(T... a)
。Collections
工具类的静态方法unmodifiableList
方法,其方法签名是public static List unmodifiableList(List extends T> list)
。这3种List的定义分别在:
ArrayList
Arrays$ArrayList
(注意是Arrays的静态内部类)Collections$UnmodifiableRandomAccessList
(注意是Collections的静态内部类)在静态方法test中,有一句Collection
,这个list是传入的形参,其类型是List
。而List
接口中的subList
方法是没有默认实现的,这意味着上面测试的3种List都是把subList
方法进行了各自的实现了的。实现的位置分别在:
ArrayList
里。Arrays$ArrayList
里,但由于没有override,所以实现在Arrays$ArrayList
的父类AbstractList
里。Collections$UnmodifiableList
里或者Collections$UnmodifiableRandomAccessList
里(都是Collections的静态内部类),由于主函数传参是new ArrayList(list))
(ArrayList继承了标记接口RandomAccess),所以静态方法unmodifiableList
方法返回的是Collections$UnmodifiableRandomAccessList
的实例。关于subList的各自实现本文不会深究,读者只需要知道List
接口中的subList
方法签名是:List
即可。
静态方法test里,就是去测试类型为List
的传入形参list,看它是否对于List
接口里的各个可选操作是否都进行了实现。当List
的实现类不支持这些可选操作时,其实现会是直接抛出UnsupportedOperationException异常。
从打印结果来看:
ArrayList
支持所有的可选操作(因为一个UnsupportedOperationException异常都没有捕获到),所以要想拥有完备的功能,还是得使用ArrayList
啊。Arrays$ArrayList
除了set()
操作外,都不支持。Collections$UnmodifiableRandomAccessList
都不支持,名副其实的Unmodifiable不可修改。public class Arrays {
/*省略*/
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);//调用静态内部类的构造器
}
/**
* @serial include
*/
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable//看起来和真正的ArrayList好像差不多
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;//内部实现只是个数组
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
@Override
public int size() {
return a.length;
}
@Override
public Object[] toArray() {
return a.clone();
}
@Override
@SuppressWarnings("unchecked")
public <T> 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;
}
@Override
public E get(int index) {
return a[index];//依靠数组的取下标操作
}
@Override
public E set(int index, E element) {
E oldValue = a[index];
a[index] = element;
return oldValue;
}
@Override
public int indexOf(Object o) {
E[] a = this.a;
if (o == null) {
for (int i = 0; i < a.length; i++)
if (a[i] == null)
return i;
} else {
for (int i = 0; i < a.length; i++)
if (o.equals(a[i]))
return i;
}
return -1;
}
@Override
public boolean contains(Object o) {
return indexOf(o) != -1;
}
@Override
public Spliterator<E> spliterator() {
return Spliterators.spliterator(a, Spliterator.ORDERED);
}
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
for (E e : a) {
action.accept(e);
}
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
E[] a = this.a;
for (int i = 0; i < a.length; i++) {
a[i] = operator.apply(a[i]);
}
}
@Override
public void sort(Comparator<? super E> c) {
Arrays.sort(a, c);
}
}
/*省略*/
}
Arrays.asList返回了一个fixed-size固定大小的List,它继承AbstractList
时除了实现了必要的get() & size()
外,还实现了set()
方法,毕竟set()
方法不会违背fixed-size这个概念。
但是这个静态内部类没有去overrideadd() & remove()
,这意味着它将使用继承于AbstractList
的实现——直接抛出UnsupportedOperationException异常。所以在使用Arrays.asList
返回的List
时一定要注意到,add() & remove()
方法我们是不能使用的。
在toArray方法里面,可能会调用到Arrays.copyOf()
方法。
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
三目表达式里,先判断(Object)newType == (Object)Object[].class)
的返回值,这里是想判断一下传入的Class
对象的类型标签是否为Object[]
,如果是那就好办了,直接创建Object的数组——new Object[newLength]
。但其类型标签如果不是Object[]
,又想创建新数组,那就必须用到Array.newInstance
了(注意是反射包里的java.lang.reflect.Array
)。
之所以一定要用三目表达式而不直接使用Array.newInstance
,我个人猜想是:使用反射来创建数组必定有着效率的问题,而如果类型标签就是Object[]
,那么就没有必要使用反射了,直接创建Object数组就可以更快执行。
注意在给Arrays.asList传参时,不能将基本类型数组作为参数。
import java.util.Arrays;
import java.util.List;
public class test1 {
public static void main(String[] args) {
int[] myArray = { 1, 2, 3 };
List myList = Arrays.asList(myArray);
System.out.println(myList.size());
}
}
代码打印结果居然是1,而debug也看出来,myList的元素类型居然是int[3]
。之所以打印结果不是3、且myList的元素类型不是int
,是因为asList 方法签名:
public static <T> List<T> asList(T... a)
可见其形参类型是T的可变参数,虽然java的自动拆装箱配合泛型方法的类型推断,能在单个基本类型时起到作用,但是遇到了基本类型的数组时,自动拆装箱就会失效了,所以List myList = Arrays.asList(myArray)
这里,程序认为你只传入了一个参数,且这个参数的类型是int[]
。
相对的,在单个基本类型时,java的自动拆装箱就可以配合泛型方法的类型推断一起搞事情了,比如List myList = Arrays.asList(4)
这句,debug效果如下:
public static <T> List<T> unmodifiableList(List<? extends T> list) {
return (list instanceof RandomAccess ?
new UnmodifiableRandomAccessList<>(list) :
new UnmodifiableList<>(list));
}
unmodifiableList方法先判断形参list是否属于RandomAccess的,RandomAccess是一个标记接口,代表可以随机存取,比如一般List就可以随机访问(其实就是其内部实现是数组,数组取下标操作当然就是随机访问了),而Set就不可以随机访问。根据是否属于RandomAccess,返回相应的实例。
static class UnmodifiableList<E> extends UnmodifiableCollection<E>
implements List<E> {
private static final long serialVersionUID = -283967356065247728L;
final List<? extends E> list;
UnmodifiableList(List<? extends E> list) {
super(list);
this.list = list;
}
public boolean equals(Object o) {return o == this || list.equals(o);}
public int hashCode() {return list.hashCode();}
public E get(int index) {return list.get(index);}//非可选操作里,就实现了get
public E set(int index, E element) {//其他的非可选操作,都是抛异常
throw new UnsupportedOperationException();
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
public int indexOf(Object o) {return list.indexOf(o);}
public int lastIndexOf(Object o) {return list.lastIndexOf(o);}
public boolean addAll(int index, Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
throw new UnsupportedOperationException();
}
@Override
public void sort(Comparator<? super E> c) {
throw new UnsupportedOperationException();
}
public ListIterator<E> listIterator() {return listIterator(0);}
public ListIterator<E> listIterator(final int index) {
return new ListIterator<E>() {
private final ListIterator<? extends E> i
= list.listIterator(index);
public boolean hasNext() {return i.hasNext();}
public E next() {return i.next();}
public boolean hasPrevious() {return i.hasPrevious();}
public E previous() {return i.previous();}
public int nextIndex() {return i.nextIndex();}
public int previousIndex() {return i.previousIndex();}
public void remove() {//当然,ListIterator也直接抛异常,如果改成调用i成员的remove,可能不会抛异常的
throw new UnsupportedOperationException();
}
public void set(E e) {
throw new UnsupportedOperationException();
}
public void add(E e) {
throw new UnsupportedOperationException();
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
i.forEachRemaining(action);
}
};
}
public List<E> subList(int fromIndex, int toIndex) {
return new UnmodifiableList<>(list.subList(fromIndex, toIndex));
}
/**
* UnmodifiableRandomAccessList instances are serialized as
* UnmodifiableList instances to allow them to be deserialized
* in pre-1.4 JREs (which do not have UnmodifiableRandomAccessList).
* This method inverts the transformation. As a beneficial
* side-effect, it also grafts the RandomAccess marker onto
* UnmodifiableList instances that were serialized in pre-1.4 JREs.
*
* Note: Unfortunately, UnmodifiableRandomAccessList instances
* serialized in 1.4.1 and deserialized in 1.4 will become
* UnmodifiableList instances, as this method was missing in 1.4.
*/
private Object readResolve() {
return (list instanceof RandomAccess
? new UnmodifiableRandomAccessList<>(list)
: this);
}
}
/**
* @serial include
*/
static class UnmodifiableRandomAccessList<E> extends UnmodifiableList<E>
implements RandomAccess
{
UnmodifiableRandomAccessList(List<? extends E> list) {
super(list);
}
public List<E> subList(int fromIndex, int toIndex) {
return new UnmodifiableRandomAccessList<>(
list.subList(fromIndex, toIndex));
}
private static final long serialVersionUID = -2542308836966382001L;
/**
* Allows instances to be deserialized in pre-1.4 JREs (which do
* not have UnmodifiableRandomAccessList). UnmodifiableList has
* a readResolve method that inverts this transformation upon
* deserialization.
*/
private Object writeReplace() {
return new UnmodifiableList<>(list);
}
}
UnmodifiableList的实现其实就是套一个壳子而已,所以操作都依赖于构造器里传入的那个List。
UnmodifiableList是名副其实的不可修改,那些常用操作里面,它就只实现了get()
方法,连set()
都不让用。注意Collections$unmodifiableList
还继承了Collections$UnmodifiableCollection
,后者已经帮忙实现了size() isEmpty() contains() iterator()
等方法,当然它的实现风格也是让很多方法抛UnsupportedOperationException异常。
UnmodifiableRandomAccessList的实现就简单,直接继承UnmodifiableList就好了。注意这二者的区别就是:UnmodifiableRandomAccessList
代表了可随机访问,UnmodifiableList
代表了不可以随机访问。
注意到,UnmodifiableRandomAccessList有一个writeReplace
方法必然返回一个非随机访问的不可修改列表;而UnmodifiableList有一个readResolve
方法可能返回一个随机访问的不可修改列表。从这两个方法的逻辑可以理解出:随机访问的列表可以直接转换为非随机访问的列表(writeReplace
方法),但非随机访问的列表要想转换为随机访问的列表的前提是,此列表原本就是可随机访问的(readResolve
方法)。这种关系就类似于父类对象和子类对象之间的互相转换。