java怎么确保集合不可改变

java怎么确保集合不可改变

有两种方式:

  • Collections. unmodifiableCollection(Collection c) 方法创建的集合
  • 使用Arrays.asList创建的集合

那么为什么这两种方式创建的集合就不能修改呢? 下边通过源码来看一下到底是为什么。

Collections. unmodifiableCollection(Collection c) 方法

List<Integer> list = new ArrayList<>();
list.add(1);
Collection<Integer> readOnlyList = Collections.unmodifiableCollection(list);
readOnlyList.add(2); // 会报错,UnsupportedOperationException()

源码解析

进入unmodifiableCollection 方法,可以看到该方法是Collections的一个静态方法,内部返回一个Collections的内部类UnmodifiableCollection。

public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) {
        return new UnmodifiableCollection<>(c);
}

进入Collections.UnmodifiableCollection这个内部类,该内部类实现了Collection接口,是调用了该内部类的构造方法,首先做了个非null判断,然后将参数赋值给了该内部类的成员变量c(Collection类型),既然实现了Collection 接口,就要实现其抽象方法,size()、 isEmpty() 、contains(Object var1)、toArray()、iterator()、 add(E var1)、remove(Object var1) 等等。主要来看看涉及到修改的add以及remove等的一些方法。通过该内部类创建的集合调用add,或者remove方法时候报错,看到期内部实现如下:

// 局部源码
  public int size() {
            return this.c.size();
        }
        public boolean isEmpty() {
            return this.c.isEmpty();
        }
        public boolean contains(Object var1) {
            return this.c.contains(var1);
        }
        public Object[] toArray() {
            return this.c.toArray();
        }
        public boolean add(E var1) {
            throw new UnsupportedOperationException();
        }
        public boolean remove(Object var1) {
            throw new UnsupportedOperationException();
        }
        public boolean addAll(Collection<? extends E> var1) {
            throw new UnsupportedOperationException();
        }
        public boolean removeAll(Collection<?> var1) {
            throw new UnsupportedOperationException();
        }

可以看到如add()、remove() 等涉及修改集合的内部直接抛出了异常 ,其他方法均是正常实现。也就是说内部类UnmodifiableCollection也是集合的一种。同样实现了Collection集合的方法,只不过在比如add、remove等修改的方法中直接抛出UnsupportedOperationException()异常,因此实现了集合不能修改的功能。

使用Arrays.asList创建的集合

List<Integer> integers = Arrays.asList(11, 22, 33, 44);
integers.add(55);		// 会报错,UnsupportedOperationException()

源码解析
Arrays.asList 同样第一步进入 arrays的静态方法asList,同样是返回了一个Arrays的静态内部类ArrayList。进入可以看到:通过其构造方法,构造了一个对象,其成员变量的值就是传进来的数组。Arrays的静态内部类ArrayList集成了AbstractList抽象类,所以该内部类其实是一个集合类。这的内部类ArrayList和我们通常使用的ArrayList不是同一个集合类,但两者同属于AbstractList抽象类的子类。

下面是部分源码,方法里的实现用…代替,只展示了方法声明

 private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, Serializable {
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;
        ArrayList(E[] var1) {....}
        public int size() {....}
        public Object[] toArray() {....}
        public <T> T[] toArray(T[] var1) {....}
        public E get(int var1) {....}
        public E set(int var1, E var2) {....}
        public int indexOf(Object var1) {....}
        public boolean contains(Object var1) {....}
        public Spliterator<E> spliterator() {....}
        public void forEach(Consumer<? super E> var1) {....}
        public void replaceAll(UnaryOperator<E> var1) {....}
        public void sort(Comparator<? super E> var1) {....}
    }

可以看到,该内部类ArrayList中并没有重写父类AbstractList中的add和remove等方法
这里我们可以根据java中的多态,父类引用指向子类实现。该引用只可以调用子类中继承或重写的父类方法,不可以调用子类中独有的方法,当然父类中独有的方法是可以调用的。那么现在这种情况就是调用了父类独有的方法了,因此初步判定:上述的异常应该是父类AbstractList抛出的。
查看父类源码,可以看到有UnsupportedOperationException(),而且父类的add方法里边就是一个空实现,及子类想要使用add、remove等方法,必须要自己重写其方法,否则直接抛出该异常。

public void add(int index, E element) {
	throw new UnsupportedOperationException();
}

这里顺便看一下“传统”ArrayList中对其父类的add等方法的重写
可以看到ArrayList类重写了父类的方法,在java多态的调用中直接调用的就是子类的方法。

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++;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值