最近在学习《Java核心技术》集合的时候,才知道集合中视图的概念,在此做下记录,加深印象.. 在jdk中,我们对Collections和Arrays等包装类并不陌生,它提供了一些静态方法对集合的操作非常有用,比如
//生成一个有三个元素为“A”的List集合。
List<String> nlist = Collections.nCopies(3,"A");
但是这个返回的list并不是传统的集合对象,而是视图对象,它实现了List接口,不需要付出建立数据结构的开销.包括Collections.singleton(“a”)等方法都返回的是视图,而不是集合类。视图对象可以说是具有限制的集合对象。这里的集合nlist 具有访问数组元素set,get的方法。但是如果调用改变数组的方法就会抛出异常。
还有其它一些视图,它具有不同的操作权限。
子范围视图:
集合类可以为自己创建一个子范围的视图,可以将任何操作应用于子范围视图上,并且子视图subList 的任何操作都会反映到list 集合中
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
System.out.println(list); // a,b,c
List<String> subList = list.subList(0, 1);
subList.clear();
System.out.println(list); //b,c
不可修改视图:
上面产生的视图是可以修改的,如果想产生不可修改的视图,可以用
List<String> strings = Collections.unmodifiableList(a);
产生不可修改的视图。
同步视图:
如果多个线程访问集合,会造成同步问题,可以通过Collections的静态方法封装成一个具有同步访问方法的集合类。
List<String> strings1 = Collections.synchronizedList(a);
这里只是小总结一下,重要的还是视图的概念,具体可以见书上详细描述。
2017.3.29 视图不可修改的原理分析
在Arrays的工具类中,有一个数组变成List的方法
List<String> string = Arrays.asList(new String[]{"a", "b", "c"});
这里要注意,string是不可修改的List,我们看asList的内部代码
public static List asList(T... a) {
return new ArrayList<>(a);
}
返回的是ArrayList,可能会问,这没问题啊,为什么不能修改呢?此ArrayList非java.util包下,而是Arrays的内部类,它同样继承AbstractList,具有LIst的数据结构.
//java.util.Arrays.ArrayList
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
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[] 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 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 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);
}
}
但是,AbstractList源码中,add,remove等方法是直接抛UnsupportedOperationException异常的,而在Arrays的ArrayList中,没有重写AbstractList抛出异常的代码。导致这种类型的ArrayList是只读的。
//java.util.AbstractList
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*
* This implementation always throws an
* {@code UnsupportedOperationException}.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
throw new UnsupportedOperationException();
}
在java.util.ArrayList中,包括读写的方法都重写了
//java.util.ArrayList
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
*
* @param index the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
那如果你确实需要 Arrays的方法从数组转换成List,而且又需要修改这个list,你可以用java.util.ArrayList构造类重新封装这个不可修改视图。
List<String> string = Arrays.asList(new String[]{"a", "b", "c"});
ArrayList<String> strings2 = new ArrayList<>(string);