集合中视图与包装器概念,及Arrays.asList不可修改的原理解释

来源:互联网 发布:身份证psd源码 编辑:程序博客网 时间:2024/06/10 14:56

最近在学习《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,cList<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 <T> List<T> asList(T... a) {   return new ArrayList<>(a);}

返回的是ArrayList,可能会问,这没问题啊,为什么不能修改呢?此ArrayList非java.util包下,而是Arrays的内部类,它同样继承AbstractList,具有LIst的数据结构.

//java.util.Arrays.ArrayListprivate 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> 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);    }}

但是,AbstractList源码中,add,remove等方法是直接抛UnsupportedOperationException异常的,而在Arrays的ArrayList中,没有重写AbstractList抛出异常的代码。导致这种类型的ArrayList是只读的。

//java.util.AbstractListpublic void add(int index, E element) {    throw new UnsupportedOperationException();}/** * {@inheritDoc} * * <p>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.ArrayListpublic 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 workreturn oldValue;}

那如果你确实需要 Arrays的方法从数组转换成List,而且又需要修改这个list,你可以用java.util.ArrayList构造类重新封装这个不可修改视图。

List<String> string = Arrays.asList(new String[]{"a", "b", "c"});ArrayList<String> strings2 = new ArrayList<>(string);
0 0
原创粉丝点击