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

最近在学习《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 <T> List<T> 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> 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.AbstractList

public 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.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);

转载于:https://www.cnblogs.com/lovejj1994/p/7182159.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值